什么是原始类型,为什么我们不应该使用它?

时间:2010-05-05 02:48:53

标签: java generics raw-types

问题:

  • Java中的原始类型是什么,为什么我经常听说不应该在新代码中使用它们?
  • 如果我们不能使用原始类型,它有什么替代方案?它是如何更好的?

15 个答案:

答案 0 :(得分:687)

答案 1 :(得分:57)

  

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

原始类型是Java语言的古老历史。起初有Collections,他们只持有Objects而且没有更少。 Collections上的每项操作都需要从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

旧的无类型集合无法强制执行类型安全,因此程序员必须记住他在集合中存储的内容。
为了解决这个限制而发明的泛型,开发人员会声明存储类型一次,而编译器会这样做。

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;}
}

请注意,使用原始类型实现CompareAblecompareTo(MyCompareAble)的接口是不可能的。 为什么你不应该使用它们:

  • Object中存储的任何Collection必须先投放才能使用
  • 使用泛型启用编译时检查
  • 使用原始类型与将每个值存储为Object
  • 相同

编译器的作用: 泛型是向后兼容的,它们使用与原始类型相同的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);

如果您直接使用原始类型,这与您编写的代码相同。我以为我不确定CompareAble界面会发生什么,我猜它会创建两个compareTo个函数,一个执行MyCompareAble而另一个执行Object并传递铸造后的第一个。

原始类型有哪些替代方法:使用generics

答案 2 :(得分:24)

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

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

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

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

如果省略实际的类型参数,则创建原始类型Box<T>

Box rawBox = new Box();

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

原始类型显示在遗留代码中,因为许多API类(例如Collections类)在JDK 5.0之前不是通用的。使用原始类型时,您基本上会获得预先通用行为 - Box会为您提供Object。为了向后兼容,允许将参数化类型分配给其原始类型:

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)

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

Type Erasure部分提供了有关Java编译器如何使用原始类型的更多信息。

未选中的错误消息

如前所述,在将遗留代码与通用代码混合时,您可能会遇到类似于以下内容的警告消息:

  

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

     

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

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

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

    static Box createBox(){
        return new Box();
    }
}

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

使用-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

答案 3 :(得分:18)

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

您应该指定type-parameter。

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

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

答案 4 :(得分:16)

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

例如,在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。这一事实是仿制药的主要驱动因素。它将运行时错误的来源更改为可在编译时检查的内容。

答案 5 :(得分:12)

编译器要你写这个:

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

因为否则,您可以将任何类型的类型添加到list中,使实例化为new ArrayList<String>()毫无意义。 Java泛型仅是编译时功能,因此使用new ArrayList<String>()创建的对象将很乐意接受IntegerJFrame元素(如果已分配给“原始类型”{{1}的引用) - 对象本身对它应该包含的类型一无所知,只有编译器才知道。

答案 6 :(得分:11)

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

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

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

如果我们不能使用原始类型,它有什么替代方案?它是如何更好的?

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

例如,对于程序员想要确保名为“names”的List变量只包含字符串的方法:

List<String> names = new ArrayList<String>();
names.add("John");          // OK
names.add(new Integer(1));  // compile error

答案 7 :(得分:11)

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

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类型为String的对象。这意味着它只能包含String类型的Object。

严格到String而不是原始类型,所以它永远不会发出警告。

    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<String> 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类型的引用变量ArrayList引用。

<案例3

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

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

它会在其中添加任何类型的Object,因为arr是原始类型。

  

警告: - Strict类型对象被raw类型引用变量引用。

答案 8 :(得分:8)

raw -type是使用泛型类型时缺少类型参数

不应使用Raw-type,因为它可能会导致运行时错误,例如将double插入到Set int的{​​{1}}中。

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

Set检索内容时,您不知道会发生什么。假设您希望它全部为int,您将其投射到Integer; <{1}} 3.45出现时运行时的异常。

类型参数添加到double后,您将立即收到编译错误。这种先发制人的错误可让您在运行期间爆炸之前解决问题(从而节省时间和精力)。

Set

答案 9 :(得分: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)。

答案 10 :(得分:5)

所说的是listList未指定的对象。那就是Java不知道列表中的对象是什么类型。然后,当您想要迭代列表时,您必须转换每个元素,以便能够访问该元素的属性(在本例中为String)。

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

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

答案 11 :(得分:4)

tutorial page

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

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

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

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

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

Box rawBox = new Box();

答案 12 :(得分:1)

我在做了一些示例练习后发现了这个页面并且有着完全相同的困惑。

==============我从这个代码中提供了样本提供的内容===============

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());
    }

====================== To此代码====================== ==

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个小时才能消除哲学......

答案 13 :(得分:0)

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

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

答案 14 :(得分:0)

避免使用原始类型

  

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

例如

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

在JDK 1.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