【问题标题】:Java: Bounded heterogeneous collections when items are not relatedJava:项目不相关时的有界异构集合
【发布时间】:2010-11-14 04:06:54
【问题描述】:

如果我有异构集合,我确切知道要放置的类型,是否有办法强制执行此操作。

例如,假设我有一个地图,它有一个字符串键和值,可以是三种不相关的类型。现在我知道我只会放 ClassA 和 ClassB 或 java.lang.String

例如这里是代码

public HetroCollection
{
    public Map<String,Object> values;
}

public ClassA
{
}

public ClassB
{
}

public static void Main(String args[])
{
   HetroCollection collection = new HetroCollection();
   collection.values.add("first", new ClassA());
   collections.values.add("second", new ClassB());
   collections.values.add("third" , "someString");
   //BAD want to stop random adds
   collections.values.("fourth" , new SomeRandomClass());
}

我想到的选项是:

  • 让类实现一个通用接口并在 Map 上使用泛型(问题是如果这还涉及 JDK 或第三方库类,那么更改类不是一种选择

  • 隐藏地图并提供参数化的 put 方法

    put(String key , ClassA 值); put(String key , ClassB 值); 放(字符串键,字符串值); 获取(字符串键);

  • 重新考虑设计,不要使用异构集合(不知道我会如何表示这一点)

为此寻找最佳实践答案。

【问题讨论】:

  • 对不起,不是答案,而是观察:似乎没有什么喜欢异构集合。每次我需要做这样的事情时,我永远不会快乐。在 java 中,您的选择是有限的。我可能会倾向于您的第二个选项 - 基本上隐藏地图 - 有特定的方法来处理它。

标签: java generics collections


【解决方案1】:

如果情况允许,我认为“最佳实践”解决方案是您的第一和第三选择。

另一个你没有考虑过的选项是这样的:

public class MyMap extends HashMap<String, Object> {
    ...
    // constructors
    ...
    @Override
    public void put(String key, Object value) {
        if (value instanceof ClassA || value instanceof ClassB) {
            super.put(key, value);
        } else {
            throw new IllegalArgumentException("Verbotten!");
        }
    }
    ...
}

您可以将此与您的第二个选项结合使用,以便有一个静态类型选项,甚至可能将 put(String, Object) 方法标记为已弃用以阻止其使用。

最后,可以选择忽略问题,并依靠应用程序程序员不要将随机的东西放入地图中。根据具体情况,这甚至可能是最好的解决方案。

【讨论】:

    【解决方案2】:

    嗯,你已经证明你的第一个想法是不可行的。如果你真的需要这个功能,第二个想法将是最好的选择。否则,最好的选择是重新考虑您的方法。但是,如果我们知道一点上下文,就更容易提供帮助。

    【讨论】:

      【解决方案3】:

      还有第四个选项:

      如果您想将这些类型的实例粘贴到一个集合中,它们很可能有一些共同点。如果你不能引入一个通用的超类型来表达这种共性,你仍然可以引入一个具有这种通用超类的并行类层次结构,并声明你的映射来保存该类型的项目。

      // You can find a better name ;-)
      abstract class Foo {
          public abstract void foo();
      
          public void bar() {
              // something generic
          }
      
          public abstract void visit(FooVisitor visitor);
      }
      
      class ClassAFoo {
          final ClassA delegate;
      
          // Constructor and implementations for foo()
      
      }
      
      class ClassBFoo {
          final ClassB delegate;
      
          // Constructor and implementations for foo()
      
      }
      
      class StringFoo {
          final String delegate;
      
          // Constructor and implementations for foo()
      }
      

      优点:

      1. 静态类型安全
      2. 您可以为普通类型添加方法或实现访问者模式以打开包装值的类型
      3. 编译器可以检查您在使用映射时是否处理了所有类型(与使用一系列 if 语句来打开类型相反)

      缺点:

      1. 样板代码,复杂性

      【讨论】:

        【解决方案4】:

        Java 中有一个很好的工具来处理这个称为类。鉴于您的示例,您可能会这样写:

        public class Foo {
          private ClassA first;
          private ClassB second;
          private String someString;
        
          ...
        
          public void setFirst(ClassA first) {
            this.first = first;
          }
        
          public ClassA getFirst() {
            return first;
          }
        
          ...
        }
        

        说真的,鉴于您所说的,这听起来正是您想要的。如果您只想允许特定键,其值可能仅属于特定类型(取决于键本身)......那是class。如果您有非常充分的理由需要在此处使用String 映射键(这对我来说似乎不太可能),请解释一下。

        编辑:

        当我回答这个问题时,出于某种原因,我的印象是您只需要强制将特定键映射到特定类型的值。再看一遍,好像不是这样的。如果不是这种情况,我认为您最好的选择是重新考虑设计(举例说明为什么需要这样做可能会有所帮助)。如果您这样做并且没有提出任何建议,我认为#2是最好的选择。它以某种类型安全的方式强制您对映射可以具有的值的类型进行限制。

        【讨论】:

        • 虽然我赞扬您考虑类,但您定义的类似乎很奇怪。如果调用者在 Foo 对象上调用 setFirst() 和 setSecond() 是什么意思?您将如何确保完全调用了 setter?确实有必要每个 Foo 包含 3 个字段,其中 2 个应该始终为 null?
        • @meriton:嗯?我真的不知道为什么您认为在任何给定时间都应该只设置一个字段。问题中没有任何内容表明这一点。即使 if 是这种情况(这看起来很奇怪),总是可以让每个 setter 将其他 2 个字段清空
        • +1 因为这将业务规则封装到一个类中。您可能还需要包含一个额外的验证方法,该方法跟踪 ClassA、ClassB 和 ClassOther 的实例是否都存在。当对象完全填充后,您将其添加到地图中。整洁而简单。当业务规则更改时,您会更新您的类,并且更改仅限于该类。
        【解决方案5】:

        理论上,使用函数式 Java 中的 HList 可以实现 List 中混合对象的类型安全。请参阅 blog postExamples。同样来自 IBM developerworks 的相关 this article。我写的是理论上的,因为实际上类型声明只能处理有限数量的元素,而且增长很快。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-03-11
          • 1970-01-01
          • 1970-01-01
          • 2014-02-22
          • 2013-07-13
          • 1970-01-01
          • 2014-05-07
          • 1970-01-01
          相关资源
          最近更新 更多