【问题标题】:How to cast a complete list without iterating through it in Javajava - 如何在不迭代Java的情况下转换完整列表
【发布时间】:2009-07-24 13:41:42
【问题描述】:

假设我有这门课:

public class BaseJob{

String name;

public void setName(String name){
this.name=name;
}
public String getName()
{
   return name;
}
}

和另一个扩展它的类:

public class DetailedJob extends BaseJob{

public void doThing();

}

此外,我在另一个类中有这个方法:

List<BaseJob> getSomeJobs() 

现在,我的问题是:

如果我确定返回的每个 BaseJob 确实是DetailedJob,是否可以避免在返回的getSomeJobs 列表中按顺序转换每个项目?

换一种说法,除了以下之外还有其他解决方案来投射列表中的所有项目:

List<BaseJob> baseJobList = getSomeJobs(); 
List<DetailedJob> detailedJobList = new ArrayList<DetailedJob>();
for (BaseJob baseJob : baseJobList)
    detailedJobList.add((DetailedJob) baseJob);

【问题讨论】:

    标签: java collections


    【解决方案1】:

    您可能想要做的是参数化定义getSomeJobs 的类。

    public final class JobHolder<T extends BaseJob> {
        public List<T> getSomeJobs() {
            ...
    

    通常未经检查的演员表表明存在设计问题。在某些情况下,例如低级实现和处理序列化时,它们是不可避免的。

    【讨论】:

    • 这似乎是一个不错的解决方案。不幸的是,我不能更改您命名的 JobHolder 的代码,
    • 这将用作:JobHolder jobHolder = new JobHolder();列出 工作 = jobHolder.getSomeJobs();
    【解决方案2】:

    如果您知道所有工作都将是详细工作,为什么要将它们放在基本工作的数组列表中?没有理由这样做,而且这种方法可以消除许多可能的错误和异常。

    【讨论】:

    • 因为持有 getSomeJobs 的类仅适用于 BaseJob,并且不想知道这些 baseJobs 是 detailJobs 还是其他任何内容。
    • 但是它们是基础作业,它们可以用于基础作业可以做的任何事情。
    【解决方案3】:

    嗯,有:

    List<BaseJob> baseJobList = getSomeJobs(); 
    @SuppressWarnings("unchecked")
    List<DetailedJob> detailedJobList = (List) baseJobList;
    

    这样做的缺点是,如果列表中的任何作业不是详细作业,则只有在有人尝试获取它时才会抛出异常。此外,如果之后将新的非详细工作添加到 baseJobList,则可能会搞砸使用 detailedJobList 的任何人。基本上你已经失去了很多类型安全。在某些情况下你可能不在乎,但这不是你应该轻易做的事情。

    【讨论】:

    • 我很想给出这个答案,但这真的很危险,正如你所说的那样
    • 演员表应该是(List) 而不是(List&lt;DetailedJob&gt;)。否则,它不会编译。
    • 对于不可能的演员表,您通常需要双重演员表。
    • 这对我有用,即使我了解类型安全问题。谢谢。
    【解决方案4】:

    您可以创建一个参数化的 getSomeJobs 方法来接受一个参数,该参数表明您知道一切都是DetailedJob,这意味着它将返回DetailedJob 列表而不是基类。

    如果你使用instanceof,你甚至不需要强制转换,你只需询问每个元素是否是DetailedJob 的一个实例,然后继续往下走。然而,这几乎不比遍历每个对象并进行强制转换更好。

    【讨论】:

      【解决方案5】:

      虽然它不能直接解决您的投射问题,但我会尝试在“其他类”上使用两种方法:

      List<BaseJob> getAllJobs();
      

      List<DetailedJob> getDetailedJobs();
      

      这使您的代码对使用“其他类”的任何人都更具可读性,并有望防止错误。

      要么这样,要么我会像@Tom Hawtin 建议的那样泛化“其他类”。

      【讨论】:

        【解决方案6】:

        提供getSomeJobs 方法的其他类应该实现一个接口(以帮助您进行单元测试等)。我们称之为JobProvider。您可以声明接口,使其始终生成扩展基本作业的内容列表,并且在您知道您的作业始终属于某个子类型的子类中,您可以在那里缩小类型定义。

        interface JobProvider {
           List<? extends BaseJob> getSomeJobs();
        }
        
        class JobProviderImpl implements JobProvider {
           public List<DetailedJob> getSomeJobs() {
              // do stuff and return
           }
        }
        

        现在,在其他代码中,如果您知道您正在处理 JobProviderImpl,您可以对其进行大小写并知道该列表将仅包含 DetailedJobs。

        if (provider instanceof JobProviderImpl) {
           List<DetailedJob> detailedJobs = ((JobProviderImpl) provider).getSomeJobs();
        }
        

        【讨论】:

          【解决方案7】:

          制作 getSomeJobs() 或编写另一个函数 getSomeDetailedJobs() 返回 列出 而不是 List 。我不知道我们还能如何“确定”所有元素都是DetailedJobs类型。

          【讨论】:

          • ...直到我看到 Jon Skeet 的回复!
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-01-09
          • 2012-01-11
          • 1970-01-01
          • 2017-01-28
          • 2011-02-02
          • 1970-01-01
          • 2019-10-20
          相关资源
          最近更新 更多