【问题标题】:Cannot access first() element from an empty list when using Grails SortedSet使用 Grails SortedSet 时无法从空列表访问 first() 元素
【发布时间】:2010-09-27 19:51:00
【问题描述】:

我正在动态创建一些 grails 域对象,然后将它们添加到另一个 grails 域对象中声明的 SortedSet。我创建了一个 Project 类,填写了它的值,并检查了它是否有效。它是有效的,所以我想将此项目添加到员工。

我的代码基本上是这样的

Employee employee = Employee.get(session.empid)
...
//populate some Project objects
...
//add projects to employee
employee.addToProjects(project)

这里可能出了什么问题?如果我执行 project.validate(),然后检查错误,则只有一个说该项目没有与之关联的有效员工 - 但是一旦我执行了 employee.addToProjects,这应该会消失。 Employee hasMany Project 对象,它是这样声明的:

class Employee implements Comparable
{
    static hasMany = [projects:Project]

    static constraints = 
    {
    }

    static mapping = {
        projects cascade:"all,delete-orphan", lazy:false
    }

    SortedSet<Project> projects = new TreeSet<Project>();
}


public class Project implements Comparable
{  
    static belongsTo = [employee:Employee]

    static hasMany = [roles:Role]

    static mapping = {
          roles lazy:false, cascade:"all,delete-orphan"
    }

    @XmlElement
    List<Role> roles = new ArrayList<Role>();


    /*
     * return sorted list.  overwriting default getter was causing error upon saving multiple roles.
     *
     */
    def List getSortedRoles(){
        Collections.sort(roles, new RoleComparator());
        return roles;
    }


    String toString()
    {
        return name
    }


    // compare by latest date of roles, then by name + id
    //if this is too intrusive, implement comparator with this logic and sort on rendering page
       int compareTo(obj) {
           if(obj == null){
               return 1;
           }

           def myMaxRole = findMaxRole(roles);
           def rhsMaxRole = findMaxRole(obj.roles);

           def rcomparator = new RoleComparator();

           System.out.println(myMaxRole.title + " " + rhsMaxRole.title + " " + rcomparator.compare(myMaxRole, rhsMaxRole));
           return rcomparator.compare(myMaxRole, rhsMaxRole);
       }

    def List getExpandableRoleList()
    {
        return LazyList.decorate(roles, FactoryUtils.instantiateFactory(Role.class));
    }


    def setExpandableRoleList(List l)
    {
        return roles = l;
    }

        def Role findMaxRole(roles){
            RoleComparator rc = new RoleComparator();

            Role maxRole = roles.first();
            for(role in roles){
                if(rc.compare(maxRole, role) > 0){
                    maxRole = role;
                }
            }

            return maxRole;
        }

public class Role implements Comparable
{

    static belongsTo = [project:Project]
    static hasMany = [roleSkills:RoleSkill,roleTools:RoleTool]

    static mapping = {
        duties type:"text"
        roleSkills cascade:"all,delete-orphan", lazy:false
        roleTools cascade:"all,delete-orphan", lazy:false

    }

    static contraints = {
        endDate(nullable: true)
    }

    boolean _deleted
    static transients = ['_deleted']

    @XmlElement
    String title = ""
    @XmlElement
    String duties = ""
    @XmlElement
    int levelOfEffort
    @XmlElement
    Date startDate = new Date()
    @XmlElement
    Date endDate = new Date()
    @XmlElement
    Date lastModified = new Date()
    @XmlElement
    LocationType locationType = new LocationType(type: "Unknown")
    @XmlElement
    String rank
    @XmlElement
    List<RoleSkill> roleSkills = new ArrayList<RoleSkill>()
    @XmlElement
    List<RoleTool> roleTools  = new ArrayList<RoleTool>()

    String toString()
    {   
        return title;
    }

    int compareTo(obj) {

        return title.compareTo(obj.title)
    }

    def skills() {
        return roleSkills.collect{it.skill}
    }
    def tools() {
        return roleTools.collect{it.tool}
    }
}

【问题讨论】:

  • 域对象是什么样的?您的项目对象是否实现了 Comparable?
  • 添加了相关信息 - 是的,它们实现了可比性
  • 另外 - 我将再次指出,当我创建 Project 对象时,它返回的所有内容都有效,除了对员工没有价值,但是当我将项目添加到 Employee 中的 Set 时应该添加

标签: java grails


【解决方案1】:

在我看来,让 [].first() 抛出 'java.util.NoSuchElementException: Cannot access first() element' from an empty List 似乎“不合时宜”。

我使用 groovy 的安全引用运算符 (?) 来避免 NPE/NoSuchElementException

def list=[]
println list[0] //returns null
println list.first()    //NoSuchElementException
println list?.first()  //NoSuchElementException.  Would prefer it to return null.
def list2=[null,3,6]
println list2.first()  // returns null
println list2[0]       //returns null

.first() 是唯一一个抛出异常的常规列表方法,这似乎令人沮丧。有没有其他人经历过这个?

应更改文档以澄清这一点。 list[0] - 如果未找到元素,则返回 null。当第一个元素恰好为空时,不区分空列表和大小写。

.first() 仅当存在第一个元素时才返回第一个元素,否则抛出 NoSuchElementException

【讨论】:

  • 阿门!为什么要为列表为空的绝对非异常事件抛出异常。
  • 我也希望为[].first() 获得null 或至少让[]?.first() 返回null... 否则我必须像def tt = [].size()&gt;0?[].first():null 一样检查
  • 问题是 []?.first() 有效,但有条件地测试列表是否为空,而不是第一个元素是否为空。如果您希望 first() 在列表为空时返回 null,那么问题是,无论列表为空还是第一个元素是否为 null,这都是模棱两可的。当然你可以这样做: [].isEmpty() ? null : [].first(),它明确地说明了你想要什么。
  • 我会提出这样的论点,即如果您重组代码,如果您在列表上测试 isEmpty() 作为处理空列表可能性的方法,您最终可能会得到更好的代码,而不是忽略这种可能性,或者希望?在某些时候处理它。再说一次,这是主观的和风格问题,groovy 倾向于忽略问题。
【解决方案2】:

回到基础并使用您的对象编写了一个集成测试,一切正常,您的错误一定是您如何保存对象

测试sn-p

void testSomething() {
    def emp = new Employee(first:"Aaron", last:"Saunders")
    emp.save()

    emp =  Employee.get(1)

    emp.addToProjects(new Project(name:"Project 3"))
    emp.addToProjects(new Project(name:"Project 1"))
    emp.addToProjects(new Project(name:"Project 2"))

    emp.save()

    println Employee.get(1)

    println Employee.get(1).projects.first()
}

我的对象..

public class Project implements Comparable
{  
    static belongsTo = [employee:Employee]

    String name;

    static mapping = {
          roles lazy:false, cascade:"all,delete-orphan"
    }


    String toString()
    {
        return name
    }


    // compare by latest date of roles, then by name + id
    //if this is too intrusive, implement comparator with this logic and sort on rendering page
       int compareTo(obj) {
           if(obj == null){
               return 1;
           }


           return this.name.compareTo(obj.name);
       }

}

class Employee implements Comparable
{
    static hasMany = [projects:Project]

    String first, last
    static constraints = 
    {
    }

    static mapping = {
        projects cascade:"all,delete-orphan", lazy:false
    }

    SortedSet<Project> projects = new TreeSet<Project>();

    int compareTo(obj) {
        if(obj == null){
            return 1;
        }
           return this.name.compareTo(obj.name);
    }

}

【讨论】:

  • 我会比较一下这个和我的。 Project对象的创建我没有贴出来,因为比我贴的要多,而且我要制作几个子对象来创建项目
【解决方案3】:

first() 是 groovy 的一部分。在空集上调用 first 将抛出 no such element 异常。

将项目添加到员工,然后尝试保存员工。

尝试使用如下代码保存:

if(!employee.save()) 
    employee.errors.allErrors.each {
        println it
    }

尝试删除该行:SortedSet projects = new TreeSet();

【讨论】:

  • 据我所知,异常发生在employee.addToProjects(proj) 行,它永远不会进入save() 函数。
  • 另外,当我取出 SortedSet 行时,我的表得到了 fubar'd 或其他东西,我不得不重新创建员工,因为我收到一条关于 first() 尝试返回 null 的不同消息加载员工
猜你喜欢
  • 2021-07-20
  • 1970-01-01
  • 2021-06-13
  • 1970-01-01
  • 2022-08-03
  • 1970-01-01
  • 2016-11-05
  • 1970-01-01
  • 2017-09-02
相关资源
最近更新 更多