6

这个问题与我的上一个问题部分相关。

我有一个代表通用对象集合的通用类:

public interface MyObject<N extends Number>{}

public interface MyCollecion<N extends Number> {
    public foo(MyObject<N> obj)
}

在我的设计中,这些对象集合是由客户端类(我们称之为 CollectionBuilder)通过抽象类方法构建的:

public interface AbstractCollectionFactory {
    public abstract <N extends Number> MyCollection<MyObject<N>> createCollection(String collectionType);

}

应以这种方式构造通用集合:

public class CollectionBuilder{
    AbstractCollectionFactory f;
    public void buildDoubleCollection(){

         MyCollection<MyObject<Double>> c = f.<Double>createCell("MyType");
    }
    public void buildIntegerCollection(){...}
}

好的。既然这里一切都好。CollectionBuilder 知道要指定的通用具体类型是什么(在这种情况下为 Double),并且可以正确构建。我可以在没有警告的情况下编译它,并且一切都应该正常工作。

现在我有一个与泛型和我的程序设计有关的问题。

我的应用程序中有另一个类需要使用由 CollectionBuilder 构建的集合(我们称这个类为 UserClass)。用户类:

  1. 不需要知道哪个特定的具体类型是它的集合(MyCollection<MyObject<Double>>MyCollection<MyObject<Integer>>)。
  2. 对这些集合执行一些操作,调用 MyCollection 接口中定义的一些方法。
  3. 我不想向它添加泛型类型。

在所描述的情况下,不参数化泛型类 MyCollection insied UserClass 是不是一个坏主意?

public UserClass{
     MyCollection a;  //warning 
     MyCollection<?> b; //no warning

     public method(MyObject o){  //warning
          a.foo(b); //compile       
     }
     public method2(MyObject<?> o){
          b.foo(o); //error do not compile
     }
}

如果我没有指定泛型参数,Java 编译器总是会发出警告。无论如何,从 UserClass 内部我不知道具体参数(我想不知道),甚至用“?”声明它。不允许我在 MyCollection 上调用方法 foo。

所以问题是:

  1. 不参数化对泛型类型的引用总是一件坏事?
  2. 如果 1 的答案是否定的,这是不这样做的正确情况吗?
  3. 如果 1 的答案是肯定的,我如何在 UserClass 内部使用 MyCollection 方法而不知道它们的泛型类型?
  4. 我的设计不好吗?

我希望已经清楚了。几天来我一直在努力解决这个问题,我不知道。请帮我。

4

4 回答 4

6
  1. 根据 Java 编译器,是的。这个答案包含很多关于为什么的优点。我个人在某些情况下不同意,尤其是在涉及使用的情况下Class(例如,如果您有Map<Class, Object>)。在这种情况下,必须始终<?>添加到“类”的末尾,这感觉就像是不必要的乏味,并且不会使代码更具可读性。

    对我来说,根据与之关联的对象类型进行参数化的想法Class一开始总是让人有点怀疑。关键Class是要有一个通用接口,该接口可用于获取有关任何对象(ala 反射)的信息,无论其类型如何。

    但我离题了。根据 Java 编译器和泛型类型的实现者,您应该始终参数化您的引用,即使您只使用<?>.

  2. 不适用。尽管您总是不能声明任何类型信息,然后@SuppressWarnings("unchecked")与显式转换一起使用以使编译器满意。

  3. 见波西米亚人的回答。

  4. 没有足够的信息可以说。虽然在我的脑海中,我不确定实际用例是什么“一个代表泛型对象集合的泛型类”。为什么不直接使用集合?

于 2011-09-06T23:31:34.897 回答
4

几乎总有一种方法可以参数化您的代码。简而言之,我会输入 UserClass,如下所示:

public UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }
} 

编辑
如果您担心使用泛型污染您的客户端代码,您可以创建一个抽象类型化类并提供非类型化实现,如下所示:

public abstract UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }
} 

public class DoubleUserClass extends UserClass<Double> {}

为了减少类膨胀,您可以将非类型化类放在基类中:

public abstract UserClass <N extends Number> {
     MyCollection<N> c;

     public method(MyObject<N> o){
          c.foo(o);      
     }

     public static class DoubleImpl extends UserClass<Double> {}
     public static class FloatImpl extends UserClass<Float> {}
     public static class IntegerImpl extends UserClass<Integer> {}
} 

并像这样使用它们:new UserClass.DoubleImpl()

于 2011-09-06T23:21:25.917 回答
2

泛型很容易被带走。你必须在某个地方停下来。所以不,我不认为不参数化对泛型类型的引用总是一件坏事。有时,使所有泛型完全正确所需的额外努力是不值得的。

但是,当然,最好尽可能指定泛型。在您的示例中,您可以按照 Bohemian 的建议向 UserClass 添加一个通用参数。

至于你的收藏是否是一个糟糕的设计:你在这里定义的接口/基础设施似乎过于笼统。有什么MyCollection不能简单做的java.util.List<YourClass>?真的需要MyObject接口吗?如果您想“标记”某种类型的类,您当然可以更具体地描述这些对象与其他对象的不同之处Object吗?

于 2011-09-06T23:30:50.760 回答
2
 MyCollection<?> b; 

 public method2(MyObject<?> o){
      b.foo(o); 
 }

如果有一个安全的带外保证b.foo(o),那么我们需要让这个调用起作用。引入辅助方法

<N extends Number>
void foo(MyCollection<N> collection, MyObject<?> obj)
{
    @SuppressWarnings("unchecked")
    MyObject<N> obj2 = (MyObject<N>)obj;

    collection.foo(obj2);
}

MyCollection<?> b;
void method2(MyObject<?> o){
     foo(b, o); // now works
}

由于带外保证,从MyObject<?>to的演员表是安全的。MyObject<N>从这个意义上说,它是一个检查型强制转换,因此我们有理由取消该警告。

在现实世界的应用程序中,我们对类型关系的了解比语言所能表达的更多,这并不罕见。如果程序员比编译器更了解他的代码,他不需要道歉。

涉及泛型的转换有点棘手;它的foo(b,o)工作原理并不直观。

可以使用原始类型MyCollection, MyObject来避免麻烦。忽略不应使用原始类型的警告。那是胡说八道。

于 2011-09-07T01:10:49.167 回答