代码之家  ›  专栏  ›  技术社区  ›  fastcodejava

没有泛型类型的未处理异常类型Throwable[重复]

  •  1
  • fastcodejava  · 技术社区  · 6 年前

    问题:

    • Java中的原始类型是什么,为什么我经常听到不应该在新代码中使用它们?
    • 如果我们不能使用原始类型,那么还有什么选择呢?怎样才能更好呢?
    0 回复  |  直到 8 年前
        1
  •  707
  •   Solomon Ucko    7 年前

    什么是生的?

    Java语言规范定义了 原始类型 具体如下:

    JLS 4.8 Raw Types

    原始类型定义为:

    • 引用类型,该类型是由不带附带类型参数列表的泛型类型声明的名称构成的。

    • 元素类型为原始类型的数组类型。

    • 非- static 原始类型的成员类型 R 不是从 .

    下面是一个示例:

    public class MyType<E> {
        class Inner { }
        static class Nested { }
    
        public static void main(String[] args) {
            MyType mt;          // warning: MyType is a raw type
            MyType.Inner inn;   // warning: MyType.Inner is a raw type
    
            MyType.Nested nest; // no warning: not parameterized type
            MyType<Object> mt1; // no warning: type parameter given
            MyType<?> mt2;      // no warning: type parameter given (wildcard OK!)
        }
    }
    

    在这里, MyType<E> 是一个 参数化类型 ( JLS 4.5 ). 通俗地说这种类型是很简单的 MyType 简而言之,但严格地说,这个名字是 我的类型<E> .

    mt 由上述定义中的第一个项目符号点具有原始类型(并生成编译警告); inn 还有一个原始类型的第三个要点。

    MyType.Nested 不是参数化类型,即使它是参数化类型的成员类型 我的类型<E> ,因为它是 静止的 .

    mt1 ,和 mt2 都是用实际类型参数声明的,因此它们不是原始类型。


    生的有什么特别的?

    本质上,原始类型的行为与引入泛型之前一样。也就是说,以下内容在编译时是完全合法的。

    List names = new ArrayList(); // warning: raw type!
    names.add("John");
    names.add("Mary");
    names.add(Boolean.FALSE); // not a compilation error!
    

    上面的代码运行得很好,但是假设您还拥有以下代码:

    for (Object o : names) {
        String name = (String) o;
        System.out.println(name);
    } // throws ClassCastException!
      //    java.lang.Boolean cannot be cast to java.lang.String
    

    现在我们在运行时遇到麻烦,因为 names 包含一些不是 instanceof String .

    如果你想的话 姓名 仅包含 String ,你 能够 也许仍然使用原始类型 手动检查 add 你自己,然后 手动铸造 字符串 来自的每个项目 姓名 甚至更好 ,但不使用原始类型 让编译器为您做所有的工作 ,利用Java泛型的能力。

    List<String> names = new ArrayList<String>();
    names.add("John");
    names.add("Mary");
    names.add(Boolean.FALSE); // compilation error!
    

    当然,如果你 希望 姓名 允许 Boolean ,则可以声明为 List<Object> names ,上面的代码将被编译。

    另见


    原始类型与使用 <Object> 作为类型参数?

    以下是引用自 有效的Java第2版,第23条:不要在新代码中使用原始类型 :

    原始类型的区别是什么 List 以及参数化类型 List<Object> ? 松散地说,前者选择了泛型类型检查,而后者明确地告诉编译器它能够保存任何类型的对象。当你能通过一个 List<String> 类型的参数 列表 ,不能将其传递给类型为的参数 列表<对象> . 泛型有子类型规则,并且 列表<字符串> 是原始类型的子类型 列表 ,但不是参数化类型 列表<对象> . 因此, 如果使用原始类型 列表 ,但如果使用参数化类型 列表<对象> .

    为了说明这一点,考虑以下方法 列表<对象> 并附加一个 new Object() .

    void appendNewObject(List<Object> list) {
       list.add(new Object());
    }
    

    Java中的泛型是不变的。一个 列表<字符串> 不是 列表<对象> ,因此下面将生成编译器警告:

    List<String> names = new ArrayList<String>();
    appendNewObject(names); // compilation error!
    

    如果你已经宣布 appendNewObject 采取原始类型 列表 作为参数,这将编译,因此您将失去从泛型获得的类型安全性。

    另见


    原始类型与使用 <?> 作为类型参数?

    列表<对象> , 列表<字符串> 等等都是 List<?> ,所以说他们只是 列表 相反。但是,有一个主要的区别:因为 List<E> 仅定义 add(E) ,不能将任意对象添加到 列表<?> . 另一方面,由于原始类型 列表 没有类型安全,您可以 添加 差不多什么都可以 列表 .

    请考虑前一个片段的以下变体:

    static void appendNewObject(List<?> list) {
        list.add(new Object()); // compilation error!
    }
    //...
    
    List<String> names = new ArrayList<String>();
    appendNewObject(names); // this part is fine!
    

    编译器做了很好的工作来保护您不可能违反 列表<?> ! 如果已将参数声明为原始类型 List list ,则代码将被编译,并且您将违反 List<String> names .


    原始类型是该类型的擦除

    返回JLS 4.8:

    可以用作 抹去 参数化类型或删除其元素类型为参数化类型的数组类型。 这种类型称为 原始类型 .

    [...]

    原始类型的超类(分别是superinterfaces)是泛型类型的任何参数化的超类(superinterfaces)的擦除。

    构造函数、实例方法或非构造函数的类型- 静止的 原始类型的字段 C 不是从其超类或超接口继承的是原始类型,该类型对应于 C类 .

    简单地说,当使用原始类型时,构造函数、实例方法和非- 静止的 字段是 也被删除 .

    举个例子:

    class MyType<E> {
        List<String> getNames() {
            return Arrays.asList("John", "Mary");
        }
    
        public static void main(String[] args) {
            MyType rawType = new MyType();
            // unchecked warning!
            // required: List<String> found: List
            List<String> names = rawType.getNames();
            // compilation error!
            // incompatible types: Object cannot be converted to String
            for (String str : rawType.getNames())
                System.out.print(str);
        }
    }
    

    当我们用生的时候 我的类型 , getNames 也将被删除,以便返回原始 列表 !

    JLS 4.6 继续解释以下内容:

    类型擦除还将构造函数或方法的签名映射到没有参数化类型或类型变量的签名。 删除构造函数或方法签名 s 是由与 s公司 以及删除 s公司 .

    如果方法或构造函数的签名被删除,则方法的返回类型和泛型方法或构造函数的类型参数也将被删除。

    删除泛型方法的签名没有类型参数。

    下面的bug报告包含编译器开发人员Maurizio Cimadamore和JLS作者之一Alex Buckley关于为什么会发生这种行为的一些想法: https://bugs.openjdk.java.net/browse/JDK-6400189 . (简而言之,它使规范更简单。)


    如果不安全,为什么允许使用原始类型?

    以下是JLS 4.8的另一段引述:

    只允许使用原始类型作为对遗留代码兼容性的让步。 强烈禁止在Java编程语言中引入泛型后编写的代码中使用原始类型。Java编程语言的未来版本可能不允许使用原始类型。

    有效Java第2版 还需要补充:

    既然你不应该使用原始类型,为什么语言设计者允许它们?提供兼容性。

    Java平台即将进入第二个十年,当泛型被引入时,存在大量的Java代码,它们不使用泛型。所有这些代码都是合法的,并且可以与使用泛型的新代码进行互操作,这一点被认为是至关重要的。必须合法地将参数化类型的实例传递给设计用于普通类型的方法,反之亦然。这一要求称为 迁移兼容性 ,推动了支持原始类型的决定。

    总之,原始类型不应该在新代码中使用。 您应该始终使用参数化类型 .


    没有例外吗?

    不幸的是,由于Java泛型是非具体化的,所以在新代码中必须使用原始类型的情况有两个例外:

    • 类文字,例如。 List.class ,不是 List<String>.class
    • instanceof 操作数,例如。 o instanceof Set ,不是 o instanceof Set<String>

    另见

        2
  •  58
  •   jiaweizhang josefx    9 年前

    Java中的原始类型是什么,为什么我经常听到不应该在新代码中使用它们?

    原始类型是Java语言的古老历史。开始的时候 Collections 他们认为 Objects 一无是处。上的每个操作 收藏 所需的强制转换 Object 到所需的类型。

    List aList = new ArrayList();
    String s = "Hello World!";
    aList.add(s);
    String c = (String)aList.get(0);
    

    虽然这在大多数情况下都起作用,但确实发生了错误

    List aNumberList = new ArrayList();
    String one = "1";//Number one
    aNumberList.add(one);
    Integer iOne = (Integer)aNumberList.get(0);//Insert ClassCastException here
    

    旧的无类型集合无法强制执行类型安全,因此程序员必须记住他在集合中存储的内容。
    泛型(Generics)是为了绕过这个限制而发明的,开发人员只需声明一次存储类型,编译器就可以代替它。

    List<String> aNumberList = new ArrayList<String>();
    aNumberList.add("one");
    Integer iOne = aNumberList.get(0);//Compile time error
    String sOne = aNumberList.get(0);//works fine
    

    供比较:

    // Old style collections now known as raw types
    List aList = new ArrayList(); //Could contain anything
    // New style collections with Generics
    List<String> aList = new ArrayList<String>(); //Contains only Strings
    

    比较复杂的界面:

    //raw, not type save can compare with Other classes
    class MyCompareAble implements CompareAble
    {
       int id;
       public int compareTo(Object other)
       {return this.id - ((MyCompareAble)other).id;}
    }
    //Generic
    class MyCompareAble implements CompareAble<MyCompareAble>
    {
       int id;
       public int compareTo(MyCompareAble other)
       {return this.id - other.id;}
    }
    

    注意,不可能实现 CompareAble compareTo(MyCompareAble) 与原始类型。 为什么不应该使用它们:

    • 任何 对象 存储在 Collection 必须先铸造才能使用
    • 使用泛型可以启用编译时检查
    • 使用原始类型与将每个值存储为 对象

    编译器的作用: 泛型是向后兼容的,它们使用与原始类型相同的java类。魔法主要发生在编译时。

    List<String> someStrings = new ArrayList<String>();
    someStrings.add("one");
    String one = someStrings.get(0);
    

    将编译为:

    List someStrings = new ArrayList();
    someStrings.add("one"); 
    String one = (String)someStrings.get(0);
    

    如果直接使用原始类型,这与编写代码相同。我想我不知道会发生什么 可比较的 接口,我想它创建了两个 compareTo 函数,一个取一个 MyCompareAble 另一个拿着 对象 把它投给第一个。

    原始类型的替代品是什么:使用 generics

        3
  •  26
  •   Paul Bellora    11 年前

    原始类型是没有任何类型参数的泛型类或接口的名称。例如,给定泛型Box类:

    public class Box<T> {
        public void set(T t) { /* ... */ }
        // ...
    }
    

    创建参数化类型 Box<T> ,则为形式类型参数提供实际类型参数 T :

    Box<Integer> intBox = new Box<>();
    

    如果省略了实际类型参数,则创建 方框<T> :

    Box rawBox = new Box();
    

    因此, Box 是泛型类型的原始类型 方框<T> . 但是,非泛型类或接口类型不是原始类型。

    原始类型出现在遗留代码中,因为许多API类(比如Collections类)在JDK 5.0之前不是泛型的。当使用原始类型时,基本上会得到预泛型行为 盒子 给你 Object s.为了向后兼容,允许将参数化类型分配给其原始类型:

    Box<String> stringBox = new Box<>();
    Box rawBox = stringBox;               // OK
    

    但是,如果将原始类型分配给参数化类型,则会收到警告:

    Box rawBox = new Box();           // rawBox is a raw type of Box<T>
    Box<Integer> intBox = rawBox;     // warning: unchecked conversion
    

    如果使用原始类型调用相应泛型类型中定义的泛型方法,也会收到警告:

    Box<String> stringBox = new Box<>();
    Box rawBox = stringBox;
    rawBox.set(8);  // warning: unchecked invocation to set(T)
    

    警告显示原始类型绕过泛型类型检查,将不安全代码的捕获推迟到运行时。因此,应该避免使用原始类型。

    类型擦除部分有更多关于Java编译器如何使用原始类型的信息。

    未选中的错误消息

    如前所述,当将遗留代码与泛型代码混合时,可能会遇到与以下类似的警告消息:

    注意:Example.java使用未检查或不安全的操作。

    注意:使用-Xlint:unchecked重新编译以获取详细信息。

    当使用对原始类型进行操作的旧API时,可能会发生这种情况,如下例所示:

    public class WarningDemo {
        public static void main(String[] args){
            Box<Integer> bi;
            bi = createBox();
        }
    
        static Box createBox(){
            return new Box();
        }
    }
    

    术语“unchecked”表示编译器没有足够的类型信息来执行确保类型安全所必需的所有类型检查。默认情况下,“unchecked”警告是禁用的,尽管编译器会给出提示。要查看所有“unchecked”警告,请使用-Xlint:unchecked重新编译。

    使用-Xlint:unchecked重新编译前面的示例将显示以下附加信息:

    WarningDemo.java:4: warning: [unchecked] unchecked conversion
    found   : Box
    required: Box<java.lang.Integer>
            bi = createBox();
                          ^
    1 warning
    

    要完全禁用未选中的警告,请使用-Xlint:-unchecked标志。这个 @SuppressWarnings("unchecked") 注释抑制未选中的警告。如果你不熟悉 @SuppressWarnings 语法,见注释。

    原始来源: Java Tutorials

        4
  •  19
  •   Bozho    14 年前
     private static List<String> list = new ArrayList<String>();
    

    您应该指定类型参数。

    警告建议定义为支持的类型 generics 应该参数化,而不是使用它们的原始形式。

    List 定义为支持泛型: public class List<E> . 这允许许多类型安全的操作,这些操作是在编译时检查的。

        5
  •  16
  •   Andy White    15 年前

    Java中的“raw”类型是一个非泛型的类,它处理“raw”对象,而不是类型安全的泛型类型参数。

    例如,在Java泛型可用之前,您将使用如下集合类:

    LinkedList list = new LinkedList();
    list.add(new MyObject());
    MyObject myObject = (MyObject)list.get(0);
    

    当您将对象添加到列表中时,它不关心它是什么类型的对象,当您从列表中获取它时,您必须显式地将它转换为所需的类型。

    使用泛型,可以删除“未知”因素,因为必须显式指定列表中可以进入的对象类型:

    LinkedList<MyObject> list = new LinkedList<MyObject>();
    list.add(new MyObject());
    MyObject myObject = list.get(0);
    

    注意,对于泛型,您不必强制转换来自get调用的对象,该集合是预定义的,仅用于MyObject。这正是泛型的主要驱动因素。它将运行时错误的源更改为可以在编译时检查的内容。

        6
  •  12
  •   Bert F    15 年前

    什么是原始类型?为什么我经常听到不应该在新代码中使用它们?

    “原始类型”是使用泛型类而不为其参数化类型指定类型参数,例如使用 List 而不是 List<String> . 当泛型被引入Java时,几个类被更新为使用泛型。使用这些类作为“原始类型”(不指定类型参数)允许遗留代码仍然编译。

    “原始类型”用于向后兼容。不建议在新代码中使用它们,因为使用带有类型参数的泛型类可以实现更强的类型化,这反过来可能提高代码的可理解性,并导致更早地发现潜在问题。

    如果我们不能使用原始类型,那么还有什么选择呢?怎样才能更好呢?

    首选的替代方法是按预期使用泛型类-使用适当的类型参数(例如。 列表<字符串> ). 这允许程序员更具体地指定类型,向未来的维护人员传达关于变量或数据结构的预期用途的更多含义,并允许编译器实施更好的类型安全性。这些优点结合在一起可以提高代码质量,并有助于防止引入一些编码错误。

    例如,对于程序员希望确保名为“names”的列表变量只包含字符串的方法:

    List<String> names = new ArrayList<String>();
    names.add("John");          // OK
    names.add(new Integer(1));  // compile error
    
        7
  •  12
  •   Michael Borgwardt    14 年前

    编译器希望您编写:

    private static List<String> list = new ArrayList<String>();
    

    否则,你可以添加任何你喜欢的类型 list ,使实例化为 new ArrayList<String>() 毫无意义。Java泛型只是一个编译时特性,因此 新建ArrayList<字符串>() 会欣然接受 Integer JFrame 元素(如果分配给“原始类型”的引用) List -对象本身不知道它应该包含什么类型,只有编译器知道。

        8
  •  12
  •   Aluan Haddad Vikrant Kashyap    7 年前

    在这里,我正在考虑多个案例,通过这些案例你可以澄清这个概念

    1. ArrayList<String> arr = new ArrayList<String>();
    2. ArrayList<String> arr = new ArrayList();
    3. ArrayList arr = new ArrayList<String>();
    

    案例1

    ArrayList<String> arr 它是一个 ArrayList 类型为的引用变量 String 指的是 ArralyList 类型的对象 字符串 . 这意味着它只能保存字符串类型的对象。

    这是一个严格的 字符串 不是原始类型,所以它永远不会发出警告。

        arr.add("hello");// alone statement will compile successfully and no warning.
    
        arr.add(23);  //prone to compile time error.
         //error: no suitable method found for add(int)
    

    案例2

    在这种情况下 ArrayList<字符串>arr 是一个严格的类型,但你的对象 new ArrayList(); 是原始类型。

        arr.add("hello"); //alone this compile but raise the warning.
        arr.add(23);  //again prone to compile time error.
        //error: no suitable method found for add(int)
    

    在这里 arr 是一种严格的类型。因此,在添加 integer .

    警告 :-一 Raw 类型对象被引用到 Strict 类型引用变量 阵列列表 .

    案例3

    在这种情况下 ArrayList arr 是原始类型,但您的对象 new ArrayList<String>(); 是一种严格的类型。

        arr.add("hello");  
        arr.add(23);  //compiles fine but raise the warning.
    

    它将向其中添加任何类型的对象,因为 阿里尔 是原始类型。

    警告 :-一 严格的 类型对象被引用到 raw 键入引用的变量。

        9
  •  8
  •   The Nail Lars Andren    9 年前

    一个 未经加工的 -类型是缺少 类型参数 使用泛型类型时。

    不应使用原始类型,因为它可能导致运行时错误,如插入 double 变成了一个 Set 属于 int s。

    Set set = new HashSet();
    set.add(3.45); //ok
    

    当从 ,你不知道会发生什么。假设你希望 内景 s,你把它投给 Integer ;当 双重的 3.45来了。

    用一个 类型参数 添加到您的 ,将立即出现编译错误。这个抢占性错误允许您在运行时发生故障之前修复问题(从而节省时间和精力)。

    Set<Integer> set = new HashSet<Integer>();
    set.add(3.45); //NOT ok.
    
        10
  •  6
  •   GuyPaddock    6 年前

    这是另一个生吃的例子:

    public class StrangeClass<T> {
      @SuppressWarnings("unchecked")
      public <X> X getSomethingElse() {
        return (X)"Testing something else!";
      }
    
      public static void main(String[] args) {
        final StrangeClass<String> withGeneric    = new StrangeClass<>();
        final StrangeClass         withoutGeneric = new StrangeClass();
        final String               value1,
                                   value2;
    
        // Compiles
        value1 = withGeneric.getSomethingElse();
    
        // Produces compile error:
        // incompatible types: java.lang.Object cannot be converted to java.lang.String
        value2 = withoutGeneric.getSomethingElse();
      }
    }
    

    正如接受的答案中提到的,您将失去对原始类型代码中泛型的所有支持。每个类型参数都转换为其擦除(在上面的示例中 Object ).

        11
  •  5
  •   pakore    14 年前

    你的意思是 list 是一个 List 未指定对象的。也就是说,Java不知道列表中是什么类型的对象。然后,当您想迭代列表时,必须对每个元素进行强制转换,以便能够访问该元素的属性(在本例中是String)。

    一般来说,参数化集合是一个更好的主意,这样您就不会有转换问题,您将只能添加参数化类型的元素,并且您的编辑器将为您提供适当的方法来选择。

    private static List<String> list = new ArrayList<String>();
    
        12
  •  4
  •   Mike    8 年前

    tutorial page .

    原始类型是没有任何类型参数的泛型类或接口的名称。例如,给定泛型Box类:

    public class Box<T> {
        public void set(T t) { /* ... */ }
        // ...
    }
    

    要创建Box的参数化类型,请为形式类型参数T提供实际类型参数:

    Box<Integer> intBox = new Box<>();
    

    如果省略了实际类型参数,则创建一个原始类型的框:

    Box rawBox = new Box();
    
        13
  •  1
  •   Pang Ajmal PraveeN    9 年前

    我做了一些简单的练习后发现了这一页。

    我从示例提供的代码开始===============

    public static void main(String[] args) throws IOException {
    
        Map wordMap = new HashMap();
        if (args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                countWord(wordMap, args[i]);
            }
        } else {
            getWordFrequency(System.in, wordMap);
        }
        for (Iterator i = wordMap.entrySet().iterator(); i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            System.out.println(entry.getKey() + " :\t" + entry.getValue());
        }
    

    对于本代码========================

    public static void main(String[] args) throws IOException {
        // replace with TreeMap to get them sorted by name
        Map<String, Integer> wordMap = new HashMap<String, Integer>();
        if (args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                countWord(wordMap, args[i]);
            }
        } else {
            getWordFrequency(System.in, wordMap);
        }
        for (Iterator<Entry<String, Integer>> i = wordMap.entrySet().iterator(); i.hasNext();) {
            Entry<String, Integer> entry =   i.next();
            System.out.println(entry.getKey() + " :\t" + entry.getValue());
        }
    
    }
    

    ===============================================================================

    这可能更安全,但花了4个小时来掩饰这一理念。。。

        14
  •  1
  •   vs97    6 年前

    避免原始类型

    原始类型是指使用泛型类型而不指定类型参数。

    例如 ,

    列表是原始类型,而 List<String> 是参数化类型。

    当在JDK1.5中引入泛型时,保留原始类型只是为了保持与旧版本Java的向后兼容性。尽管使用原始类型仍然是可能的,

    他们应该避免 :

    • 他们通常需要石膏
    • 它们不是类型安全的,有些重要的错误只会在运行时出现
    • 它们的表达能力较低,并且不能像参数化类型那样自文档 例子

      import java.util.*;
      
      public final class AvoidRawTypes {
      
      void withRawType() {
      
          //Raw List doesn't self-document, 
          //doesn't state explicitly what it can contain
      
          List stars = Arrays.asList("Arcturus", "Vega", "Altair");
      
          Iterator iter = stars.iterator();
      
          while (iter.hasNext()) {
      
              String star = (String) iter.next(); //cast needed
      
              log(star);
          }
      
      }
      
      void withParameterizedType() {
      
          List < String > stars = Arrays.asList("Spica", "Regulus", "Antares");
      
          for (String star: stars) {
      
              log(star);
          }
      
      }
      
      private void log(Object message) {
      
          System.out.println(Objects.toString(message));
      
      }
      
      }
      

    供参考 : https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html

        15
  •  0
  •   Stefan Reich    7 年前

    原始类型在表达您想要表达的内容时很好。

    例如,反序列化函数可能返回 List ,但它不知道列表的元素类型。所以 列表 是这里合适的返回类型。