繁体   English   中英

Java和Kotlin使用泛型进行投射。丢失类型安全

[英]Java & Kotlin casting with generics. Losing typesafety

在使用Kotlin / Java进行编码时,我在使用强制转换和泛型时偶然发现了一些奇怪的东西。 似乎可以让类型系统认为列表是List<Foo>类型,而它实际上是List<Object>

任何人都可以向我解释为什么这是可能的?

以下是Kotlin和Java中的一个例子:

Kotlin中的例子

fun <T> test(obj: Any): List<T> {
    val ts = ArrayList<T>()
    ts.add(obj as T)
    return ts
}

fun <T> test2(obj: Any): T {
    return obj as T
}

fun <T> test3(obj: Any): List<T> {
    val ts = ArrayList<T>()
    ts.add(test2(obj))
    return ts
}


fun main(args: Array<String>) {
    val x = test<Double>(1) // Returns a list of Integers and doesn't error
    println(x)

    val y = test2<Double>(1) // Casts the Int object to a Double.
    println(y)

    val z = test3<Double>(1) // Returns a list of Integers and doesn't error.
    println(z)
}

Java中的示例

public class Test {
    public static <T> List<T> test(Object obj){
        ArrayList<T> ts = new ArrayList<>();
        ts.add((T) obj);
        return ts;
    }

    public static <T> T test2(Object obj){
        return (T) obj;
    }

    public static <T> List<T> test3(Object obj){
        ArrayList<T> ts = new ArrayList<>();
        ts.add(test2(obj));
        return ts;
    }


    public static void main(String[] args) {
        List<Double> x = test(1); // Returns a list of Integers and doesn't error
        System.out.println(x);

        // Double y = test2(1); // Errors in java an Integers cannot be converted into a Double.
        // System.out.println(y);

        List<Double> z = test3(1); // Returns a list of Integers and doesn't error.
        System.out.println(z);
    }
}

Java没有具体化的泛型。 也就是说,通用信息在运行时不存在,并且所有通用代码都被称为擦除的过程“简化”。 当已知泛型类型以确保正确性时,编译器会抛出强制转换。 你不能转换为泛型类型,因为泛型类型不足以让运行时知道某个值是否为1,这就是为什么javac会因此而对你javac大叫,因为它知道你是要求JVM做一些它不可能做的事情,引入运行时不安全。

public class Test {
    public static List test(Object obj) { // generic types => erasure = raw types
        ArrayList ts = new ArrayList();
        ts.add(obj); // No cast: List.add has erasure (Ljava.lang.Object;)V
        return ts;
    }

    public static Object test2(Object obj) { // T is unbounded => erasure = Object
        return obj; // No cast: all types <: Object
    }

    public static List test3(Object obj) {
        ArrayList ts = new ArrayList();
        ts.add(test2(obj)); // Note: we don't know what T is, so we can't cast to it and ensure test2 returned one.
        return ts;
    }


    public static void main(String[] args) {
        List x = test(1); // Returns a list and doesn't error
        System.out.println(x);

        Double y = (Double) test2(1); // Errors in java as an Integer cannot be converted into a Double
        // This is because the compiler needs to insert casts to make generics work
        System.out.println(y);

        List z = test3(1);
        // Unlike y, there isn't a cast in test3 because test3 doesn't know what T is, so the Integer passes through, uncast, into a List<Double>.
        // The JVM can't detect this, because it doesn't even know what a List<Double> is.
        System.out.println(z);
    }
}

注意test2如何擦除一个美化的身份函数,导致test3完成与test1完全相同的事情,但具有间接级别。

执行未选中强制转换为T时,编译器会发出警告。 如果您的程序类型中出现此类警告,则无法保证安全。 所以你看到的行为是预料之中的。

尝试更改第二个测试:

    Double y = test2(1.0);
    System.out.println(y);

在你的代码test2(1) ,参数1被自动装入一个Integer ,它不能被转换为Double

暂无
暂无

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

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