What is a raw type and why shouldn't we use it?
- Java中的原始类型是什么?为什么我经常听到他们不应该在新代码中使用?
- 如果我们不能使用原始类型,那么还有什么选择呢?如何才能更好呢?
什么是原始类型?
Java语言规范定义了如下的原始类型:好的。JLS 4.8原始类型
A raw type is defined to be one of:
Ok.
The reference type that is formed by taking the name of a generic type declaration without an accompanying type argument list.
Ok.
An array type whose element type is a raw type.
Ok.
A non-
static member type of a raw typeR that is not inherited from a superclass or superinterface ofR .Ok.
下面是一个例子来说明:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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!) } } |
这里,
从本质上讲,原始类型的行为就像在引入泛型之前一样。也就是说,以下内容在编译时完全合法。好的。
1 2 3 4 |
上面的代码运行得很好,但是假设您还具有以下功能:好的。
1 2 3 4 5 |
现在我们在运行时遇到了麻烦,因为
假设您希望
1 2 3 4 | List<String> names = new ArrayList<String>(); names.add("John"); names.add("Mary"); names.add(Boolean.FALSE); // compilation error! |
当然,如果您希望
- Java教程/泛型
原始类型与使用
以下是有效Java第二版的引用,项目23:不要在新代码中使用原始类型:好的。
Just what is the difference between the raw type
List and the parameterized typeList ? Loosely speaking, the former has opted out generic type checking, while the latter explicitly told the compiler that it is capable of holding objects of any type. While you can pass aList to a parameter of typeList , you can't pass it to a parameter of typeList . There are subtyping rules for generics, andList is a subtype of the raw typeList , but not of the parameterized typeList . As a consequence, you lose type safety if you use raw type likeList , but not if you use a parameterized type likeList .Ok.
为了说明这一点,考虑以下方法,它采用一个
1 2 3 |
Java中的泛型是不变量的。
1 2 | List<String> names = new ArrayList<String>(); appendNewObject(names); // compilation error! |
如果您声明
和 有什么区别?- Java泛型(非)协方差
原始类型与使用
考虑前面代码段的以下变化:好的。
1 2 3 4 5 6 7 | static void appendNewObject(List<?> list) { list.add(new Object()); // compilation error! } //... List<String> names = new ArrayList<String>(); appendNewObject(names); // this part is fine! |
编译器做了一个很好的工作来保护您不受可能违反
回到JLS 4.8:好的。
It is possible to use as a type the erasure of a parameterized type or the erasure of an array type whose element type is a parameterized type. Such a type is called a raw type.
Ok.
[...]
Ok.
The superclasses (respectively, superinterfaces) of a raw type are the erasures of the superclasses (superinterfaces) of any of the parameterizations of the generic type.
Ok.
The type of a constructor, instance method, or non-
static field of a raw typeC that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding toC .Ok.
更简单地说,当使用原始类型时,构造函数、实例方法和非
take the following example:>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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); } } |
当我们使用RAW茶叶
4.6 continues to the following JLS解释:>
Type erasure also maps the signature of a constructor or method to a signature that has no parameterized types or type variables. The erasure of a constructor or method signature
s is a signature consisting of the same name ass and the erasures of all the formal parameter types given ins .Ok.
The return type of a method and the type parameters of a generic method or constructor also undergo erasure if the method or constructor's signature is erased.
Ok.
The erasure of the signature of a generic method has no type parameters.
Ok.
下面的错误报告cimadamore contains some思想从毛里求斯,编译器的开发,和Alex Buckley,one of the作家of the sort of this JLS,为什么在线:一个行为的occur HTTPS浏览:/ / / / bugs.openjdk.java.net - 6400189 JDK。(在EN makes the short,简单的内部规范。)>
如果这是unsafe,为什么是它允许使用的RAW型?
这里有一股来自JLS 4.8>
The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.
Ok.
Java第二版has this also effective to add:>
Given that you shouldn't use raw types, why did the language designers allow them? To provide compatibility.
Ok.
The Java platform was about to enter its second decade when generics were introduced, and there was an enormous amount of Java code in existence that did not use generics. It was deemed critical that all this code remains legal and interoperable with new code that does use generics. It had to be legal to pass instances of parameterized types to methods that were designed for use with ordinary types, and vice versa. This requirement, known as migration compatibility, drove the decision to support raw types.
Ok.
在总结不好,应该用RAW类型在美国队列。你应该总是使用parameterized types。>
有了是不?
不幸的是他/她,因为Java泛型are there are两非reified了RAW,must be used types -代码:纽约>
List.class 文字类,例如,List not.class instanceof o instanceof Set 操作数,例如,o instanceof Set not
see also
Collection 为什么是非法的?.class
好吧。
What are raw types in Java, and why do I often hear that they shouldn't be used in new code?
原始类型是Java语言的古老历史。起初有以东十一〔0〕人,他们把以东十一〔1〕一点也不多,一点也不少。在
1 2 3 4 |
虽然这在大多数时候都起作用,但确实发生了错误。
1 2 3 4 |
旧的无类型集合无法强制类型安全,因此程序员必须记住他在集合中存储的内容。为了绕过这个限制而发明的泛型,开发人员只需声明一次存储类型,编译器就会这样做。
1 2 3 4 |
作比较:
1 2 3 4 |
比较复杂的界面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //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;} } |
注意,使用原始类型的
- 任何存储在
Collection 中的Object 在使用前必须进行铸造。 - 使用泛型可以启用编译时检查
- 使用原始类型与存储每个值相同,如
Object 。
编译器的作用:泛型是向后兼容的,它们使用与原始类型相同的Java类。这种魔力主要发生在编译时。
1 2 3 | List<String> someStrings = new ArrayList<String>(); someStrings.add("one"); String one = someStrings.get(0); |
将编译为:
1 2 3 |
这与直接使用原始类型时编写的代码相同。我想我不知道
原始类型的替代品是什么:使用泛型
原始类型是没有任何类型参数的泛型类或接口的名称。例如,给定泛型Box类:
1 2 3 4 | public class Box<T> { public void set(T t) { /* ... */ } // ... } |
要创建参数化类型的
1 | Box<Integer> intBox = new Box<>(); |
如果省略实际类型参数,则创建一个原始类型的
因此,
原始类型出现在遗留代码中,因为许多API类(如集合类)在JDK5.0之前不是通用的。当使用原始类型时,基本上会得到预泛型行为-
1 2 |
但是,如果将原始类型分配给参数化类型,则会收到警告:
1 2 |
如果使用原始类型调用在相应的泛型类型中定义的泛型方法,也会收到警告:
1 2 3 | Box<String> stringBox = new Box<>(); Box rawBox = stringBox; rawBox.set(8); // warning: unchecked invocation to set(T) |
警告显示原始类型绕过了一般类型检查,将不安全代码的捕获延迟到运行时。因此,应该避免使用原始类型。
类型擦除部分有更多关于Java编译器如何使用原始类型的信息。
未选中的错误消息如前所述,在将旧代码与通用代码混合时,可能会遇到类似于以下内容的警告消息:
Note: Example.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
当使用对原始类型进行操作的旧API时,可能会发生这种情况,如下例所示:
1 2 3 4 5 6 7 8 9 10 |
术语"unchecked"意味着编译器没有足够的类型信息来执行确保类型安全所必需的所有类型检查。默认情况下,"unchecked"警告被禁用,但编译器会给出提示。要查看所有"unchecked"警告,请使用-xlint:unchecked重新编译。
使用-xlint:unchecked重新编译上一个示例将显示以下附加信息:
1 2 3 4 5 6 | WarningDemo.java:4: warning: [unchecked] unchecked conversion found : Box required: Box<java.lang.Integer> bi = createBox(); ^ 1 warning |
要完全禁用未选中的警告,请使用-xlint:-unchecked标志。
原始源代码:Java教程
1 | private static List<String> list = new ArrayList<String>(); |
您应该指定类型参数。
警告建议应参数化定义为支持泛型的类型,而不是使用其原始形式。
Java中的"原始"类型是一个非泛型的类,它处理的是"原始"对象,而不是类型安全的泛型类型参数。
例如,在Java泛型可用之前,您将使用这样的集合类:
1 2 3 | LinkedList list = new LinkedList(); list.add(new MyObject()); MyObject myObject = (MyObject)list.get(0); |
当您将对象添加到列表中时,它不关心对象的类型,当您从列表中获取对象时,您必须将其显式转换为您期望的类型。
使用泛型,可以删除"未知"因素,因为必须显式指定可以在列表中放入的对象类型:
1 2 3 | LinkedList<MyObject> list = new LinkedList<MyObject>(); list.add(new MyObject()); MyObject myObject = list.get(0); |
注意,对于泛型,您不必强制转换来自get调用的对象,集合是预先定义的,仅用于myObject。这一事实是泛型的主要驱动因素。它将运行时错误源更改为可在编译时检查的内容。
编译器希望您编写以下内容:
1 | private static List<String> list = new ArrayList<String>(); |
因为否则,您可以在
在这里,我正在考虑多个案例,通过这些案例,您可以清楚地了解这个概念。
1 2 3 |
案例1
这是对
1 2 3 4 | 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
在这种情况下,
1 2 3 | 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) |
这里,
Warning :- A
Raw Type Object is referenced to aStrict type Referenced Variable ofArrayList .
案例3
在这种情况下,
1 2 | arr.add("hello"); arr.add(23); //compiles fine but raise the warning. |
它将向其中添加任何类型的对象,因为
Warning :- A
Strict Type Object is referenced to araw type referenced Variable.
什么是原始类型?为什么我经常听到在新代码中不应该使用它们?
"原始类型"是在不为参数化类型指定类型参数的情况下使用泛型类,例如使用
"原始类型"用于向后兼容。不建议在新代码中使用它们,因为使用带有类型参数的泛型类可以实现更强的类型化,从而提高代码的可理解性,并导致更早地发现潜在的问题。
如果我们不能使用原始类型,那么还有什么选择呢?如何才能更好呢?
首选的替代方法是按预期使用泛型类-使用适当的类型参数(例如
例如,对于程序员希望确保名为"name"的列表变量只包含字符串的方法:
1 2 3 | List<String> names = new ArrayList<String>(); names.add("John"); // OK names.add(new Integer(1)); // compile error |
原始类型是使用泛型类型时缺少类型参数。
不应使用原始类型,因为它可能会导致运行时错误,例如将
当你从
在
1 2 | Set<Integer> set = new HashSet<Integer>(); set.add(3.45); //NOT ok. |
这里还有另一种情况,原始类型会咬你:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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(); } } |
正如在接受的答案中提到的,您将失去对原始类型代码中的泛型的所有支持。每一个类型参数都被转换为它的擦除(在上面的例子中,它只是
意思是你的
一般来说,参数化集合是一个更好的主意,因此您没有转换问题,您只能添加参数化类型的元素,并且您的编辑器将为您提供可供选择的适当方法。
1 | private static List<String> list = new ArrayList<String>(); |
教程页。
原始类型是没有任何类型参数的泛型类或接口的名称。例如,给定泛型Box类:
1 2 3 4 | public class Box<T> { public void set(T t) { /* ... */ } // ... } |
要创建参数化类型的框,请为形式类型参数t提供实际类型参数:
1 | Box<Integer> intBox = new Box<>(); |
如果省略实际类型参数,则创建一个原始类型的框:
我在做了一些样本练习和有完全相同的困惑之后找到了这个页面。
=======我从示例提供的代码中删除了这段代码=========
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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()); } |
对这段代码的修改==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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个小时的时间来破译哲学…
避免原始类型
Raw types refer to using a generic type without specifying a type parameter.
例如,
列表是原始类型,而
当在JDK 1.5中引入泛型时,保留原始类型,以保持与旧版本Java的向后兼容性。尽管使用原始类型仍然是可能的,
应避免:
- 他们通常需要石膏
- 它们不是类型安全的,一些重要的错误只会在运行时出现。
它们的表达能力较低,并且不会像参数化类型那样自我记录。例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40import 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
原始类型在表达您想要表达的内容时很好。
例如,反序列化函数可能返回