Java泛型:仅泛型返回类型,泛型既有返回类型又有参数类型

时间:2020-09-23 14:14:03

标签: java

从一个例子开始,

public static <T> T method(T str){
    return (T)str;
}
// T is deduced to be a String
// This fails at compile time
Integer integer = method("Trial");
//Object obj = method("Trial"); // Old example

public static <T> T method(String str){
    return (T)str;
}
// What type does T gets deduced to in this case?
// This compiles but gives an error at run-time.
Integer integer = method("Trial");
//Object obj = method("Trial"); // Old example

两个代码段都可以正常编译。在第二个示例中,T被推导为哪种类型?

4 个答案:

答案 0 :(得分:0)

未指定的通用类型被视为Object,因此在两种情况下返回的类型都是正确的。 那么使用哪种方法呢?
实际上,最精确的匹配类型将首先被接受。对于您来说,method(String str)"Trial"String

// Will rename for understanding
// method(T str) => method1
// method(String str) => method2 
public static <T> T method1(T str){
    return (T)str;
}
public static <T> T method2(String str){
    return (T)str;
}
// This is your implementation
Object obj = <Object> //as undefined
    method2("Trial"); // as mentionned above

// BUT
Object obj = <Object> // as undefined
    method1(1); // as 1 is an int upperbounded to Object

// AND
Object obj = <String> 
    method2("Trial");
Object obj = <String>
    method(1); // will not compile

您应该检查此post以获得有关优先级超载的更多详细信息

答案 1 :(得分:0)

无论如何,我还是很难过。

在您的第一个示例中,返回类型由方法参数的传入类型(即String)确定(推断)。考虑以下示例:

public static <T> T something(T something) {
    return something;
}

和两个调用示例:

String something = something("string");
Integer someOther = something("something");

第一个可以正常工作,但是第二个会出错,因为动态推断的返回类型为String,而我们尝试将其存储为Integer变量。

现在,当您将两个值都分配给Object变量时,此方法工作正常的原因是-如上所述-这是String和{{1的上限}}。实际上,您使用Integer查看了一个反汇编的类文件,您会看到方法的签名如下:

javap

请注意,由于Java的类型擦除,以上所有内容在编译后实际上将成为public static <T extends java.lang.Object> T something(T) 类型。

另一方面,您的示例还将传入的参数强制转换为Object 冗余,因为这是由编译器推断类型的能力处理的。

我建议您看看以下内容:

https://www.baeldung.com/java-type-erasure https://www.tutorialspoint.com/java_generics/java_generics_type_inference.htm

现在让我们考虑第二个示例:

在那种情况下,T将有效地达到T类型,就像我上面提到的那样。这仍然是该方法的上限,但是这里有一个警告。此方法错误,因为它接受Object对象并尝试将其(再次错误)转换为推断的类型。

例如,我可以执行此操作(编译正常),但在运行时会生成String

ClassCastException

此呼叫

public static <T> T something2(String something) {
    return (T) something;
}

现在如上所述,分配给Integer somethingOther = something2("sas"); 时起作用的原因是它是Object的父类以及其他所有内容。

答案 2 :(得分:0)

在您的情况下,第一个函数的参数数据类型和函数的返回类型均为T,这意味着它们都可以是任何类型。它们可以是字符串,整数,浮点数,字符,数组或其他任何形式。这种T类型的范围仅限于此特定功能。

第二个函数的数据类型参数为String,而返回类型为T,这意味着只能将String类型的值传递给该函数。

例如,

public static <T> T func1 (T var)
{
     return var;
}

以上函数可以具有a = func1("String")b = func1(2)或其他任何函数。 a将为String类型,b将为int类型。另一方面,

public static <T> T func2 (String var)
{
     return var;
}

将在func2(2)上返回错误。

请参阅Oracle Docs,以深入了解通用方法。

编辑为注释:调用函数时,Java编译器会自动从方法参数中推断出参数的类型。在示例中调用func1()时,我写的是func1("ABC")。 Java编译器非常聪明,现在可以理解参数T的类型为String。同样,我从func1返回一个String类型,因此编译器现在将知道该返回类型必须为String。

您还可以通过https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html学习更多内容。

答案 3 :(得分:0)

所以我在这里回答我自己的问题。已知

public static <T> T method(String str){
    return (T)str;
}
Integer x = method("String");

该方法可以很好地编译,这意味着编译器能够推断出模板参数的类型。但是,无法查看在编译过程中将其推导为哪种类型。相反,我们可以查看运行时错误。运行上面的代码时,出现以下错误,

Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer

这意味着T的类型是从分配给它的变量的类型推导出来的。所以它在内部变成了

public static Integer method(String str){
    return (Integer)str;
}

现在

public static <T> T method(T str){
    return (T)str;
}
Integer integer = method("Trial");

是另外一个故事。它不会编译。我的IDE(Intelij)抱怨no instance(s) of type variable(s) exist so that String conforms to Integer可能表示该功能变为

public static String method(String str){
    return (String)str;
}

由编译器执行。总而言之,当只有返回类型是通用类型时,则根据分配给该函数的变量的类型进行推导。当函数参数具有通用变量时,则根据传递给函数的变量进行推论。