java - 理解广告Java泛型中的真理原理

标签 java generics

我一直在尝试正确地理解Java泛型。因此,在这一探索中,我遇到了一个原则“广告中的真理原则”,我想用简单的语言来理解它。

广告中的真理原则:数组的修饰类型必须是子类型
删除其静态类型。


我已经编写了示例代码.java和.class文件,如下所示。请仔细阅读代码,并解释一下代码中的哪一部分指定/指示了以上声明的哪一部分。

我给我写了评论,我不应该在这里写代码说明。

public class ClassA {

    //when used this method throws exception
    //java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
    public static <T> T[] toArray(Collection<T> collection) {

        //Array created here is of Object type
        T[] array = (T[]) new Object[collection.size()];

        int i = 0;
        for (T item : collection) {
            array[i++] = item;
        }
        return array;
    }

    //Working fine , no exception
    public static <T> T[] toArray(Collection<T> collection, T[] array) {
        if (array.length < collection.size()) {
            //Array created here is of correct intended type and not actually Object type
            //So I think , it inidicates "the reified type of an array" as type here lets say String[]
            // is subtype of Object[](the erasure ), so actually no problem
            array = (T[]) Array.newInstance(array.getClass().getComponentType(), collection.size());
        }

        int i = 0;
        for (T item : collection) {
            array[i++] = item;
        }
        return array;
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList("A", "B");
        String[] strings = toArray(list);
//        String[] strings = toArray(list,new String[]{});
        System.out.println(strings);
    }
}

请尝试用简单的语言解释。请指出我错了。带有更多注释的更正代码将不胜感激。

谢谢你们

最佳答案

我将Java泛型和集合称为“本书”,将本书的作者称为“作者”。
由于本书在解释IMO原理方面做得很差,因此我会不止一次地对这个问题表示支持。
陈述
广告中的真理原则:
数组的修饰类型必须是擦除其静态类型的子类型。
进一步称为原理。
该原理有何帮助?

  • 跟随它,代码将编译并运行,没有异常(exception)
  • 不要遵循它,并且代码会编译,但是会在运行时引发异常。

  • 词汇
    什么是静态类型?
    应该称为引用类型
    在下面的代码中,提供的AB是类型A ref = new B();Aref的静态类型(Bref的动态类型)。学术界术语。
    数组的简化类型是什么?
    修正是指在运行时可用的类型信息。数组被认为是可修复的,因为VM知道了它们的组件类型(在运行时)。
    arr2 = new Number[30]中,arr2化类型Number[],其数组类型为组件类型Number
    一种类型的擦除是什么?
    应该被称为运行时类型
    类型参数的虚拟机 View (运行时 View )。
    提供的T是类型参数,以下代码的运行时 View
    <T extends Comparable<T>> void stupidMethod(T[] elems) {
        T first = elems[0];
    }
    
    将会
    void stupidMethod(Comparable[] elems) {
        Comparable first = elems[0];
    }
    
    这使得Comparable成为T的运行时类型。为什么是Comparable?因为那是T的最左边界。
    我要看哪种代码才能使该原则相关?
    该代码应暗示分配给数组类型的引用。左值或右值都应包含类型参数。
    例如提供的T是类型参数
    T[] a = (T[])new Object[0]; // type parameter T involved in lvalue
    
    或者
    String[] a = toArray(s); // type parameter involved in rvalue
    // where the signature of toArray is
    <T> T[] toArray(Collection<T> c);
    
    在左值或右值中不涉及任何类型参数的情况下,该原理无关紧要。
    示例1(遵循原理)
    <T extends Number> void stupidMethod(List<T>elems) {
        T[] ts = (T[]) new Number[0];
    }
    
    Q1 :ts所引用的数组的简化类型是什么?
    A1 :数组创建提供了答案:使用Number创建组件类型为new的数组。 Number[]
    Q2 :ts的静态类型是什么?
    A2 :T[] Q3 :静态ts类型的擦除是什么?
    A3 :为此,我们需要擦除T。鉴于T extends Number是有界的,所以T的擦除类型是其最左边的边界-Number。现在我们知道了T的擦除类型,对于ts的擦除类型是 Number[]
    Q4 :是否遵循本原则?
    A4 :重申问题。 A1 A3 的子类型吗?即Number[]Number[]的子类型吗?是=>这意味着遵循原则
    示例2(不遵循原理)
    <T extends Number> void stupidMethod(List<T>elems) {
        T[] ts = (T[]) new Object[0];
    }
    
    Q1 :ts所引用的数组的简化类型是什么?
    A1 :使用new创建数组,组件类型为Object,因此为 Object[]
    Q2 :ts的静态类型是什么?
    A2 :T[] Q3 :静态ts类型的擦除是什么?
    A3 :为此,我们需要擦除T。鉴于T extends Number是有界的,所以T的擦除类型是其最左边的边界-Number。现在我们知道了T的擦除类型,对于ts的擦除类型是 Number[]
    Q4 :是否遵循本原则?
    A4 :重申问题。 A1 A3 的子类型吗?即Object[]Number[]的子类型吗?否=>,这意味着未遵循
    原则。
    期望在运行时引发异常。
    示例3(不遵循原理)
    给定提供数组的方法
    <T> T[] toArray(Collection<T> c){
        return (T[]) new Object[0];
    }
    
    客户代码
    List<String> s = ...;
    String[] arr = toArray(s);
    
    Q1 :provider方法返回的数组的简化类型是什么?
    A1 :为此,您也需要查看提供方法以了解其初始化方式-new Object[...]。这意味着该方法返回的数组的修饰类型为 Object[]
    Q2 :arr的静态类型是什么?
    A2 :String[] Q3 :静态ts类型的擦除是什么?
    A3 :不涉及类型参数。擦除后的类型与静态类型 String[] 相同。
    Q4 :是否遵循本原则?
    A4 :重申问题。 A1 A3 的子类型吗?即Object[]String[]的子类型吗?否=>,这意味着未遵循原则。
    期望在运行时引发异常。
    示例4(遵循原理)
    给定提供数组的方法
    <T> T[] toArray(Collection<T> initialContent, Class<T> clazz){
        T[] result = (T[]) Array.newInstance(clazz, initialContent);
        // Copy contents to array. (Don't use this method in production, use Collection.toArray() instead)
        return result;
    }
    
    客户代码
    List<Number> s = ...;
    Number[] arr = toArray(s, Number.class);
    
    Q1 :provider方法返回的数组的简化类型是什么?
    A1 :使用从客户端收到的具有组件类型的反射创建的数组。答案是 Number[]
    Q2 :arr的静态类型是什么?
    A2 :Number[] Q3 :静态ts类型的擦除是什么?
    A3 :不涉及类型参数。擦除后的类型与静态类型 Number[] 相同。
    Q4 :是否遵循本原则?
    A4 :重申问题。 A1 A3 的子类型吗?即Number[]Number[]的子类型吗?是=>这意味着遵循原则
    有趣的名字叫什么?
    在这里乱逛。广告中的真相可能意味着出售您声明的商品。
    lvalue = rvalue我们将rvalue作为提供者,并将lvalue作为接收者。
    作者可能将提供者视为广告商。
    参照上述示例3中的提供方法,
    <T> T[] toArray(Collection<T> c){
        return (T[]) new Object[0];
    }
    
    方法签名
    <T> T[] toArray(Collection<T> c);
    
    可以看成是广告:给我一个Long的列表,我会给你一个Long的数组。
    但是,从方法主体看,实现表明该方法不是真实的,因为它创建并返回的数组是Object的数组。
    因此,示例3中的toArray方法在于其营销 Activity 。
    在示例4中,提供方法是真实的,因为签名中的语句(给我一个集合及其类型参数作为类文字,我将为您提供具有该组件类型的数组)与主体中发生的匹配。
    示例3和4具有用作广告的方法签名。
    示例1和2没有明确的广告(方法签名)。广告和规定是交织在一起的。
    但是,我认为该原则没有更好的名称。那真是个名字。
    结束语
    由于使用诸如静态类型和擦除类型之类的术语,因此我认为该原则的陈述不必要地是含糊的。分别使用引用类型和擦除后的运行时类型/类型,将使掌握Java外行变得更加容易(真正地像您一样)。
    作者指出,这本书是Java泛型上最好的书籍[0]。我认为这意味着他们所针对的受众是广泛的受众,因此,更多有关其所采用原则的示例将非常有帮助。
    [0] https://youtu.be/GOMovkQCYD4?t=53

    关于java - 理解广告Java泛型中的真理原理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36589210/

    相关文章:

    java - 如何在 android/java 中向字符串数组添加新条目?

    c# - 将泛型与可能是值或引用类型的 null 进行比较?

    Java方法重载 - 同一继承树中的通用参数和参数

    java - 为什么在我的示例中需要 try-catch with throws?

    java - Kotlin 类作为 junit 测试运行,即使它在 androidTest 包中 - 对于 Java 类它正常运行

    java - 复制 map 的线程安全方法

    java - Swing Worker 覆盖进程 <T,V>

    java - 将不同类型的集合迭代到用户定义的类型列表中

    Java 接受任何子类型作为参数

    java - 如何创建通用方法来容纳任意数量的 POJO