美文网首页
type erasure && generic types &&

type erasure && generic types &&

作者: sherrysack | 来源:发表于2018-05-14 20:40 被阅读0次

首先Java号称自己是”类型安全“语言,不会意外怀孕。

Girl g = new Boy(); //编译器爸爸不允许

为了确保类型安全,Java数组就非常龟毛:必须明确知道内部元素的类型,而且会”记住“这个类型,每次往数组里插入新元素都会进行类型检查,不匹配会抛出java.lang.ArrayStoreException。拿到下面这段代码,编译器爸爸就会说:“说好来玩的都是Integer小朋友,社会哥String不让进。”

Object[] objArray = new Integer[10];

objArray[0] = "Hello"; // ERROR: ArrayStoreException

但Java世界也有“万圣节化装舞会”。泛型是用擦除(Erasure)实现的,运行时类型参数会被擦掉。比如下面3个小朋友,擦除类型参数以后,实际元素都是Object:

List<String> strList = new ArrayList<String>(); 

List<Object> objList = new ArrayList<Object>();

List rawList = new ArrayList();

如果允许化泛型数组的话,下面的代码,编译器爸爸会无法区分"ArrayList<String>"和"ArrayList<Integer>"。

Object[] objArray = new ArrayList<String>[10]; // 假设可以创建泛型数组

objArray[0] = new ArrayList<Integer>(); // 编译器爸爸被骗,不报错

后面有同学问:那强制转型原生类型为什么可以?

List<Integer>[] genericArray = (List<Integer>[])new ArrayList[10];

因为Java虽然禁止直接创建泛型数组实例,但并没有禁止”声明一个泛型数组引用“。所以仍然可以通过强制转型原生类型数组的方式,绕过限制。但这种方法可以是可以,但编译器就无法保证代码的类型安全。编译器爸爸无法阻止下面这种意外怀孕,

List<Integer>[] genericArray = (List<Integer>[])new ArrayList[10];

genericArray[0] = new ArrayList<String>(Arrays.asList(new String[]{"Hello"})); // 意外怀孕了









                     ------------------------以下为原答案------------------------Java Language Specification明确规定:数组内的元素必须是“物化”的。It is a compile-time error if the component type of the array being initialized is not reifiable.

对“物化”的第一条定义就是不能是泛型:A type is reifiable if and only if one of the following holds:It refers to a non-generic class or interface type declaration.... ...

因为Array的具体实现是在虚拟机层面,嵌地非常深,也查不到源码。只好用javap反编译看看具体初始化数组的字节码。我们反编译下面一段代码:初始化一个String数组和一个int数组。`String[] s=new String[]{"hello"};

int[] i=new int[]{1,2,3};

`

反编译的片段如下:` Code:

   0: iconst_1

   1: anewarray     #2                  // class java/lang/String

   4: dup

   5: iconst_0

   6: ldc           #3                  // String hello

   8: aastore

   9: astore_1

  10: iconst_3

  11: newarray       int

  13: dup

  14: iconst_0

  15: iconst_1

  ... ...

`其中:"1: anewarray #2":创建String数组

"11: newarray int":创建int数组 anewarray和newarray都是虚拟机内部用来创建数组的命令。最多只能有2的8次方256个操作码,光创建数组就占了不止一个,可见数组的地位有多特殊。

其中newarray用atype来标记数组类型。anewarray用index来标记。从描述里可以看到,数组除了元素类型,还有一个必须确定的是长度,因为数组是一段连续内存。

查一下 Java Virtual Machine 对anewarray命令的描述,anewarray <type>

<type> indicates what types of object references are to be stored in the array. It is either the name of a class or interface, e.g. java/lang/String, or, to create the first dimension of a multidimensional array, <type> can be an array type descriptor, e.g.[Ljava/lang/String;比如anewarray字节码命令的格式就是anewarray后面跟一个具体的元素类型。所以不能确定<type>的确切类型,就无法创建数组。

Can I declare a reference variable of an array type whose component type is a concrete parameterized type?

Yes, you can, but you should not, because it is neither helpful nor type-safe.

You can declare a reference variable of an array type whose component type is a concrete parameterized type. Arrays of such a type must not be created. Hence, this reference variable cannot refer to an array of its type. All that it can refer to is null , an array whose component type is a non-parameterized subtype of the concrete parameterized type, or an array whose component type is the corresponding raw type. Neither of these cases is overly useful, yet they are permitted. Example (of an array reference variable with parameterized component type):

Pair<String,String>[] arr = null;  // fine 
arr = new Pair<String,String>[2] ; // error: generic array creation

The code snippet shows that a reference variable of type Pair<String,String>[] can be declared, but the creation of such an array is rejected.

But we can have the reference variable of type Pair<String,String>[] refer to an array of a non-parameterized subtype. Example (of another array reference variable with parameterized component type):

class Name extends Pair<String,String> { ... }
Pair<String,String>[] arr = new Name[2];// fine

Which raises the question: how useful is such an array variable if it never refers to an array of its type? Let us consider an example. Example (of an array reference variable refering to array of subtypes; not recommended):

void printArrayOfStringPairs( Pair<String,String>[] pa) { 
  for (Pair<String,String> p : pa) 
    if (p != null) 
      System.out.println(p.getFirst()+" "+p.getSecond());  
} 
Pair<String,String>[] createArrayOfStringPairs() { 
  Pair<String,String>[] arr = new Name[2]; 
  arr[0] = new Name("Angelika","Langer");   // fine 
  arr[1] = new Pair<String,String>("a","b");  // fine (causes ArrayStoreException) 
  return arr; 
} 
void extractStringPairsFromArray( Pair<String,String>[] arr) { 
  Name name = (Name) arr[0]; // fine 
  Pair<String,String> p1 = arr[1];    // fine 
} 
void test() { 
  Pair<String,String>[] arr = createArrayOfStringPairs (); 
  printArrayOfStringPairs (arr);  
  extractStringPairsFromArray(arr); 
}

The example shows that a reference variable of type Pair<String,String>[] can refer to an array of type Name[] , where Name is a non-parameterized subtype of Pair<String,String>[] . However, using a reference variable of type Pair<String,String>[]offers no advantage over using a variable of the actual type Name[] . Quite the converse; it is an invitation for making mistakes. For instance, in the createArrayOfStringPairs method the compiler would permit code for insertion of elements of type Pair<String,String> into the array though the reference variable of type Pair<String,String>[] . Yet, at runtime, this insertion will always fail with an ArrayStoreExceptionbecause we are trying to insert a Pair into a Name[] . The same would happen if we tried to insert a raw type Pair into the array; it would compile with an "unchecked" warning and would fail at runtime with an ArrayStoreException . If we used Name[] instead of Pair<String,String>[]the debatable insertions would not compile in the first place. Also, remember that a variable of type Pair<String,String>[]can never refer to an array that contains elements of type Pair<String,String> . When we want to recover the actual type of the array elements, which is the subtype Name in our example, we must cast down from Pair<String,String>to Name , as is demonstrated in the extractStringPairsFromArraymethod. Here again, using a variable of type Name[]would be much clearer. Example (improved):

void printArrayOfStringPairs( Pair<String,String>[] pa) { 
  for (Pair<String,String> p : pa) 
    if (p != null) 
      System.out.println(p.getFirst()+" "+p.getSecond());  
} 
Name[] createArrayOfStringPairs() { 
  Name[] arr = new Name[2] ; 
  arr[0] = new Name("Angelika","Langer");   // fine 
  arr[1] = new Pair<String,String>("a","b");  // error 
  return arr; 
} 
void extractStringPairsFromArray( Name[] arr) { 
  Name name = arr[0];                 // fine (needs no cast) 
  Pair<String,String> p1 = arr[1];    // fine 
} 
void test() { 
  Name[] arr = createArrayOfStringPairs (); 
  printArrayOfStringPairs (arr); 
  extractStringPairsFromArray(arr); 
}

Since an array reference variable whose component type is a concrete parameterized type can never refer to an array of its type, such a reference variable does not really make sense. Matters are even worse than in the example discussed above, when we try to have the variable refer to an array of the raw type instead of a subtype. First, it leads to numerous "unchecked" warnings because we are mixing use of raw and parameterized type. Secondly, and more importantly, this approach is not type-safe and suffers from all the deficiencies that lead to the ban of arrays of concrete instantiation in the first place. No matter how you put it, you should better refrain from using array reference variable whose component type is a concrete parameterized type. Note, that the same holds for array reference variable whose component type is a wildcard parameterized type. Only array reference variable whose component type is an unbounded wildcardparameterized type make sense. This is because an unbounded wildcard parameterized type is a reifiable type and arrays with a reifiable component type can be created; the array reference variable can refer to an array of its type and the deficiencies discussed above simply do not exist for unbounded wildcard arrays.

["MyHashSet","add","add","contains","contains","add","contains","remove","contains"]
[[],[1],[2],[1],[3],[2],[2],[2],[2]]

相关文章

网友评论

      本文标题:type erasure && generic types &&

      本文链接:https://www.haomeiwen.com/subject/wbxsdftx.html