繁体   English   中英

类型擦除 Generics

[英]Type erasure Generics

编译时在 new T[5] 处发生错误 => error: generic array creation 根据我的理解,该数组是在编译时创建的,因为我们在编译时不知道 T 的类型我们不能实例化一个数组。 但是,如果 T 在编译时被擦除并更改为 Object 那么为什么仍然会出现此错误? 因为我们可以创建一个 Object 的数组。

// Before Compiling 
public class GenericClass<T> {
    GenericClass(){
        T[] obj = new T[5];
    }
}

// After Compiling
public class GenericClass {
GenericClass() {
Object[] obj = new Object[5];
}
}

类似的情况,比如,

public class GenericClass<T> {
        GenericClass(){
            T obj = new T(); }}

/*  error :required: class
  found:    type parameter T
  where T is a type-variable:
  T extends Object declared in class GenericClass*/

根据我的理解,数组是在编译时创建的

不,数组是在运行时创建的。

并且因为我们在编译时不知道 T 的类型,所以我们不能实例化一个数组。

正确的。

但是,如果 T 在编译时被擦除并更改为 Object 那么为什么仍然会出现此错误?

因为“它在编译时被擦除并更改为对象”被过于简单化了。

此外,generics 和 arrays 彼此不友好。 问题是,在 generics 部分被擦除的地方,arrays不会那样工作 你可以这样做:

String[] x = new String[10];
tellMeTheTypeOfMyArray(x);

void tellMeTheTypeOfMyArray(Object[] o) {
  System.out.println("Your array's component type is: " + o.getClass().getComponentType());
}

此代码将编译并正常工作,没有错误,并打印:

Your array's component type is: java.lang.String

与 generics 相比,您不能在其中编写此类方法。 你不可能这样做:

List<String> x = new ArrayList<String>();
tellMeTheTypeOfMyList(x);

void tellMeTheTypeOfMyList(List<?> o) {
  System.out.println("Your list's component type is: " + ??????);
}

工作。 这里不可能有 java 代码,没有什么可以代替 ?????? 打印 String,因为该信息在运行时根本不存在了。

想象一下这段代码:

// This is written by Wahab today.

class Example<T> {
  protected T[] arr;

  Example() {
    this.arr = new T[10];
  }
}

它像你想要的那样工作。 然后我做:

// Written by me, a year later
class Uhoh extends Example<String> {
  Uhoh() {
    super();
  }

  void hmmm() {
    System.out.println(this.arr.getComponentType());
  }
}

我显然期望,不,要求 - 这会打印java.lang.String ,但它不可能这样做 因为这很奇怪和令人困惑,java 有一个规则:如果你编译你的代码并且你没有看到任何关于 generics 问题的警告(并且没有@SuppressWarnings将它们消除),那么这种混淆不太可能发生。

允许您编写new T[]并以这种愚蠢的方式编写new Object[]被认为太过分了。

那么如何将 arrays 与 generics 类型一起使用呢?

java.util.ArrayList相同的方式: Do not use generics here Arrays 如果您打算在通用代码中创建它们,则几乎不应该有T类型。 如果你的代码库中的任何地方都有一个T[] ,那么这意味着你永远不应该为它做任何事情——让你new代码的调用者为你做。 如果您确实想自己new new arrays,请不要使用 T,使用Object[]作为类型,并在需要时转换为 T。 这就是 java 内置 ArrayList class 的工作原理。 一些摘录直接从其来源复制粘贴:

    transient Object[] elementData; // non-private to simplify nested class access

    public E get(int index) {
        Objects.checkIndex(index, size);
        return elementData(index);
    }

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

这是一个示例,同样直接来自 ArrayList 的来源(或者更确切地说, java.util.Collection定义了它,而 ArrayList 继承了它),您让调用者为您提供代码来生成 arrays:

    default <T> T[] toArray(IntFunction<T[]> generator) {
        return toArray(generator.apply(0));
    }

在这里,调用者提供了一个 function,它将一个int转换为一个T[] - 它采用了执行new String[10]的概念并将其转换为一个 function,然后您将其传递给 toArray 方法,该方法将使用它(感觉在这里可以随意忽略它是如何使用它的,这是一个有点奇怪的解决方案。它有效,只是 - 不确定你是否应该学习那部分的课程)。

你像这样使用它:

List<String> listOfStrings = ...;
String[] convertedToArray = listOfStrings.toArray(String[]::new);

Java arrays 在运行时知道它们的组件类型。 创建数组时,必须在运行时提供组件类型。 但是在您的GenericClass中,它不能这样做,因为它在运行时不知道T是什么。 如果它创建一个Object[] ,那么 object 将具有错误的运行时 class,并且如果T不是Object ,则该实例与类型T[]不兼容。 你是对的,在 class 中,没有立即出错。 但是如果变量是T[]的声明暴露给外部 scope,它期望T是一个更具体的类型,它可能会导致ClassCastException

// Before type erasure 
class GenericClass<T> {
    T[] obj;
    GenericClass() {
        obj = new T[5]; // if hypothetically you could do this
    }
    T[] getObj() {
        return obj;
    }
}
class MyCode {
    public static void main(String[] args) {
        GenericClass<String> foo = new GenericClass<>();
        String[] strings = foo.getObj(); // no casts needed; no warnings
    }
}

// After type erasure
class GenericClass {
    Object[] obj;
    GenericClass() {
        obj = new Object[5];
    }
    Object[] getObj() {
        return obj;
    }
}
class MyCode {
    public static void main(String[] args) {
        GenericClass foo = new GenericClass();
        String[] strings = (String[]) foo.getObj(); // ClassCastException at runtime
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM