简体   繁体   English

方法签名和演员表中的泛型?

[英]Generics in method signatures and casts?

Why does this work? 为什么这样做?

@SuppressWarnings("unchecked")
public <T> T getResult(Class annotated) {
    return (T) getResult(annotated, new HashMap<String, Object>(0));
}

How does the compiler know how to cast the return value? 编译器如何知道如何转换返回值? It can't know it at compile time: it could be anything. 它在编译时无法知道它:它可能是任何东西。 Does it do it at runtime every time? 它是否每次都在运行时执行?

The type of T in the generic method is inferred from the context of a call to the method, if possible. 如果可能的 ,泛型方法中的T类型是从对方法的调用的上下文中推断出来的。

String string = foo.getResult(Something.class);

If the type of T can't be determined from the context (for example, if the result is passed directly as an argument to a method that is itself generic) you may have to specify the type yourself: 如果无法从上下文中确定T的类型(例如,如果结果直接作为参数传递给本身通用的方法),则可能必须自己指定类型:

List<String> list = Arrays.asList(foo.<String>getResult(Something.class));

Generic methods may also use the types they declare in parameters, which can force the parameters and the result type to agree: 通用方法也可以使用它们在参数中声明的类型,这可以强制参数和结果类型一致:

public <T> T getResult(Class<T> type) { ... }

String string = foo.getResult(String.class); // ok
String string = foo.getResult(Something.class); // not ok

At runtime, generics don't exist so it just tries to assign the result to whatever you're trying to assign it to... if it's the wrong type, you get a ClassCastException . 在运行时,泛型不存在,因此它只是尝试将结果分配给您尝试将其分配给的任何内容...如果它是错误的类型,则会得到ClassCastException

It's easy for the VM at runtime because T is simply Object due to type-erasure . VM在运行时很容易,因为由于类型擦除, T只是Object In other words this: 换句话说:

return (T)getResult(annotated, new HashMap<String, Object>(0));

Compiles to the same thing as this: 编译成同样的东西:

return (Object)getResult(annotated, new HashMap<String, Object>(0));

That cast is unchecked , which means that it is not checked - neither at compile time nor at runtime. 该转换是未选中的 ,这意味着它不会被检查 - 无论是在编译时还是在运行时。 To verify, compile and execute: 验证,编译和执行:

static <T> T magicCast(Class<T> clazz, Object o) {
    return (T) o;
}

public static void main(String[] args) {
    magicCast(Integer.class, "hello");
    System.out.println("The magic cast always works.");
}

Of course, that is dangerous. 当然,这很危险。 Consider: 考虑:

public class Test<T> {
    public final T foo;

    public Test(Object foo) {
        this.foo = (T) foo;
    }

    public static void main(String[] args) {
        Test<String> test = new Test<String>(42);

        // after being passed through dozens of methods     
        test.foo.startsWith("hello"); // ClassCastException     
    }
}

That travesty of a variable holding an object that is not a subtype of the declared type is known as heap pollution. 持有不是声明类型的子类型的对象的变量的歪曲被称为堆污染。 It is the reason why a compiler is mandated to emit an unchecked warning - unless somebody suppressed that warning. 这就是为什么编译器被强制发出未经检查的警告的原因 - 除非有人压制了这个警告。

It is therefore recommended to do a reflective cast where possible; 因此,建议尽可能进行反射铸造; for instance: 例如:

static <T> T magicCast(Class<T> clazz, Object o) {
    return clazz.cast(o); // reflective cast, checked at runtime
}

Of course, if you know the class at compile time, you should use an ordinary cast, because that enables to compiler to performn additional sanity checks, allowing it to reject casts like 当然,如果你在编译时知道这个类,你应该使用普通的强制转换,因为这样可以让编译器执行额外的健全性检查,允许它拒绝像

int x = (Integer) "hello";

which can never succeed. 这永远不会成功。

Unfortunately you did not write how do you invoke this method. 不幸的是你没有写你如何调用这个方法。 I believe that the code looks like: 我相信代码看起来像:

obj.getResult(); obj.getResult();

In this case the answer is simple: the compiler adds casting to MyClass and if you decompile the byte code you see something like 在这种情况下,答案很简单:编译器将转换添加到MyClass,如果您反编译字节代码,您会看到类似的内容

(MyClass)obj.getResult(); (MyClass的)obj.getResult();

The same happens if you invoke you method and assign it result to variable: MyClass result = obj.getResult(); 如果您调用方法并将结果分配给变量,则会发生相同的情况:MyClass result = obj.getResult();

By the way I think that it is a pity that you added annotation @SuppressWarining("unchecked") If T is of type "annotated" you should say: 顺便说一下,我觉得很遗憾你添加了注释@SuppressWarining(“unchecked”)如果T是“注释”类型,你应该说:

public T getResult(Class annotated) { return (T) getResult(annotated, new HashMap(0)); public T getResult(Class annotated){return(T)getResult(annotated,new HashMap(0)); } }

This is the usual way to work with generics. 这是使用泛型的常用方法。 See classes Collections, Arrays, AbstractList.toArray() etc. 请参阅类Collections,Arrays,AbstractList.toArray()等。

int y=10;
int z=12;

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

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