我想知道以下两个方法声明有什么区别:
public Object doSomething(Object obj) {....}
public <T> T doSomething(T t) {....}
有什么事情是你可以/愿意用其中一个做而另一个却不能做的吗?我在本网站的其他地方找不到这个问题。
与上下文隔离——没有区别。在
t
和 obj
上,您只能调用 Object
的方法。
但有上下文 - 如果你有一个通用类:
MyClass<Foo> my = new MyClass<Foo>();
Foo foo = new Foo();
然后:
Foo newFoo = my.doSomething(foo);
与对象相同的代码
Foo newFoo = (Foo) my.doSomething(foo);
两大优势:
Object
版本,您将无法确定该方法始终返回 Foo
。如果它返回 Bar
,您将在运行时得到一个 ClassCastException
。这里的区别在于,在第一个中,我们指定调用者必须传递一个Object实例(任何类),并且它将返回另一个Object(任何类,不一定是同一类型)。
在第二种情况下,返回的类型将与定义类时给出的类型相同。
Example ex = new Example<Integer>();
这里我们指定 T 的类型,这允许我们对类或方法施加更多约束。例如,我们可以实例化一个
LinkedList<Integer>
或 LinkedList<Example>
,并且我们知道当我们调用其中一个方法时,我们将返回一个 Integer 或 Example 实例。
这里的主要目标是调用代码可以指定类将操作的对象类型,而不是依赖类型转换来强制执行此操作。
请参阅 Oracle 的 Java 泛型*。
*更新了链接。
不同之处在于,使用泛型方法时,我不需要强制转换,并且当我做错时会收到编译错误:
public class App {
public static void main(String[] args) {
String s = process("vv");
String b = process(new Object()); // Compilation error
}
public static <T> T process(T val) {
return val;
}
}
使用我总是需要转换的对象,当我做错时我不会收到任何错误:
public class App {
public static void main(String[] args) {
String s = (String)process("vv");
String b = (String)process(new Object());
}
public static Object process(Object val) {
return val;
}
}
您不需要进行额外的班级选角。在第一种情况下,您将始终获得 java.lang.Object 类的对象,您需要将其转换为您的类。在第二种情况下,T 将被替换为通用签名中定义的类,并且不需要类转换。
在运行时,什么也没有。但在编译时,第二个将进行类型检查,以确保参数的类型和返回值的类型与 T 解析的任何类型匹配(或者是其子类型)(第一个示例也进行类型检查,但每个对象都是一个对象的子类型,因此每种类型都会被接受)。
T 是泛型类型。这意味着它可以在运行时被任何符合条件的对象替换。您可以按如下方式调用此类方法:
String response = doSomething("hello world");
或
MyObject response = doSomething(new MyObject());
或
Integer response = doSomething(31);
正如你所看到的,这里存在多态性。
但是,如果它被声明为返回 Object,则除非您键入强制类型转换,否则您无法执行此操作。
在第一种情况下,它接受任何类型的参数,例如 string 并返回 foo 类型。在第二种情况下,它接受 foo 类型的参数并返回 foo 类型的对象。
在 Java 中您可以考虑使用泛型而不是对象类型的原因如下:
就像下面的示例一样,您可以更安全地使用泛型。类型T必须扩展“BaseClass”,其他类将产生编译错误。
public class MyClass<T extends BaseClass> {
List<T> list;
}