【问题标题】:Which is the right way to create an instance of Generic class in Java?在 Java 中创建 Generic 类的实例的正确方法是什么?
【发布时间】:2009-03-25 15:23:54
【问题描述】:

在 Java 中初始化 Generic 类的实例时,在语句的两边指定 Type 有什么好处吗?

或者换一种说法,这两个有效语句有什么区别:

ArrayList<String> test = new ArrayList<String>();

和:

ArrayList<String> test = new ArrayList();

(似乎第二个语句不等于:

ArrayList<String> test = new ArrayList<Object>();

作为无效的第三条语句并导致incompatible types编译错误。)

【问题讨论】:

    标签: java generics


    【解决方案1】:

    第二个语句最终或多或少等同于第一个语句,但这只是因为泛型在运行时被删除。您会收到“未经检查的转化”警告,这就是我不喜欢它的原因。

    更好的方法是使用这样的静态泛型方法:

    public static <T> List<T> newList() {
        return new ArrayList<T>();
    }
    

    然后做

    List<String> test = newList();
    

    这就是 Google 收藏的作用。

    (而且您几乎应该始终将您的列表声明为List,而不是ArrayList。便于以后切换实现。)

    编辑: dribeas 在 cmets 中询问这两个声明之间的确切区别是什么,以及为什么我说它们“或多或少等效”。因为type erasure,它们之间的唯一区别是警告。这是比较它们的一小段代码:

    import java.util.*;
    
    class GenericDeclarationTest {
        public static void main(String[] args) {
            List<String> list1 = new ArrayList<String>();
            list1.add("");
            String s1 = list1.get(0);
            List<String> list2 = new ArrayList();
            list2.add("");
            String s2 = list2.get(0);
        }
    }
    

    这是生成的字节码(由javap -c GenericDeclarationTest 打印):

    Compiled from "GenericDeclarationTest.java"
    class GenericDeclarationTest extends java.lang.Object{
    GenericDeclarationTest();
      Code:
       0:   aload_0
       1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
       4:   return
    
    public static void main(java.lang.String[]);
      Code:
       0:   new     #2; //class java/util/ArrayList
       3:   dup
       4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
       7:   astore_1
       8:   aload_1
       9:   ldc     #4; //String
       11:  invokeinterface #5,  2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
       16:  pop
       17:  aload_1
       18:  iconst_0
       19:  invokeinterface #6,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
       24:  checkcast       #7; //class java/lang/String
       27:  astore_2
       28:  new     #2; //class java/util/ArrayList
       31:  dup
       32:  invokespecial   #3; //Method java/util/ArrayList."<init>":()V
       35:  astore_3
       36:  aload_3
       37:  ldc     #4; //String
       39:  invokeinterface #5,  2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
       44:  pop
       45:  aload_3
       46:  iconst_0
       47:  invokeinterface #6,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
       52:  checkcast       #7; //class java/lang/String
       55:  astore  4
       57:  return
    
    }
    

    如您所见(如果您有耐心的话),两者是相同的。

    顺便说一句,这在 Java 7 中可能会变得更容易。Project Coin 中有一个 proposal 用于“用于通用实例创建的改进类型推断”。如果它进行最后的剪辑,语法将是:

    List<String> test = new ArrayList<>();
    // or
    Map<String, Object> test2 = new HashMap<>();
    

    打字不会太难吧?

    【讨论】:

    • 代码中“静态”之后的 是故意的吗?它的目的是什么?
    • @euphoria83: 是为了让编译器知道它是一个泛型方法。请参阅java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf 的第 7 页及以下内容。
    • 有什么区别:List x = new ArrayList();和你的中间功能?当您说第一个和第二个示例或多或少等效时,有什么区别?
    • 代码编译后几乎是一样的,但无论如何你都应该使用第一种方法,否则javac会给你警告。中间函数将检测到您正在寻找 List 并返回一个。这样,您只需指定一次
    • 改进的类型推断的主要好处不是减少了输入,而是输入了两次相同的东西(因此犯错的机会更少)。
    【解决方案2】:

    这种冗余很烦人。您可能希望查看具有工厂方法的 Google Collections,它们可以像这样创建 Lists:

    List<Double> doubleList = Lists.newLinkedList();
    

    还要注意Java Generics are not covariant

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-22
      • 2012-06-01
      • 2012-05-13
      • 1970-01-01
      • 2010-09-06
      • 2021-08-23
      • 2011-04-12
      相关资源
      最近更新 更多