【问题标题】:Java Generics: Generic Return Type Only and Generic both Return Type and Parameter TypeJava 泛型:仅泛型返回类型和泛型返回类型和参数类型
【发布时间】:2020-09-24 03:26:25
【问题描述】:

从一个例子开始,

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

两个代码 sn-ps 编译良好。第二个例子中的 T 推导到了哪一种类型?

【问题讨论】:

  • 问题是?
  • 阅读编译器消息,它们会告诉你问题的真相。
  • 有没有办法可以看到实例化的模板?

标签: java


【解决方案1】:

未指定的通用类型被视为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 以了解有关重载优先级的更多详细信息

【讨论】:

    【解决方案2】:

    我很难回答这个问题,但无论如何。

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

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

    还有两个调用示例:

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

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

    现在,当您将两个值都分配给Object 变量时,它工作正常的原因是——如上所述——这是StringInteger 的上限。实际上,在您使用 javap 查看反汇编的类文件时,您会看到您的方法的签名如下所示:

    public static <T extends java.lang.Object> T something(T)
    

    请注意,由于 Java 的类型擦除,上述所有内容在编译后将有效地最终成为 Object 类型。

    在另一个旁注中,您的示例还将传入的参数转换为T,即 多余的,因为这是由编译器推断类型的能力处理的。

    我建议你看看以下内容:

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

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

    在那一个中​​,T 将有效地达到Object 类型——正如我上面提到的。这仍然是该方法的上限,但这里有一个警告。此方法错误,因为它接受 String 对象并尝试将其(再次错误)强制转换为推断类型。

    例如,我可以这样做(编译正常)但在运行时生成ClassCastException

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

    还有这个电话:

    Integer somethingOther = something2("sas");
    

    现在如上所述,分配给Object 的原因是它是String 的父类以及其他所有内容。

    【讨论】:

    • 你还没有提到第二个例子中 T 被推导成什么类型​​?是整数类型吗?
    【解决方案3】:

    在您的案例中,第一个函数同时具有参数的数据类型和函数的返回类型为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 以详细了解通用方法。

    Edit to Comment : 当一个函数被调用时,Java 编译器会自动从方法参数中推断出参数的类型。在我的示例中调用func1() 时,我写了func1("ABC")。 Java 编译器足够智能,现在可以理解参数的T 类型是String。同样,我从func1 返回一个字符串类型,所以编译器现在知道返回类型必须是字符串。

    您也可以通过https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html了解更多。

    【讨论】:

    • 您好,感谢您的回答,但这不是我想要问的。
    • 在第二个示例中,当您执行Integer a = func("hello"); 时,它会编译。编译器必须推断出T 的类型。我问第二个例子是什么。
    • @nmd_07 请参阅编辑后的答案,如果它现在回答您的问题,请告诉我。
    猜你喜欢
    • 1970-01-01
    • 2016-09-16
    • 1970-01-01
    • 1970-01-01
    • 2016-11-13
    • 2016-10-16
    • 2016-12-24
    • 2021-05-01
    • 1970-01-01
    相关资源
    最近更新 更多