当我注意到以下代码编译时没有警告并打印Integer / String
时,我感到非常惊讶:
public final class GenericsTest {
private static <T> void method(T arg1, T arg2) {
System.out.println(arg1.getClass().getSimpleName());
System.out.println(arg2.getClass().getSimpleName());
}
public static void main(String[] args) {
method(1, "1");
}
}
我预计会出现编译错误。
此代码编译是否有原因?
确保参数具有相同类型的正确方法是什么?
编辑:有界类型参数怎么样?我能想到的最好的是:
private static <T, U extends T> void method(T arg1, U arg2) {
System.out.println(arg1.getClass().getSimpleName());
System.out.println(arg2.getClass().getSimpleName());
}
不幸的是,java不允许循环约束。 <T extends U, U extends T>
无法编译。这是死路一条吗?
答案 0 :(得分:7)
这个编译的原因是因为Java会在{{1}之后推断传入的参数的最具体的超类型,在这种情况下, Object
Serializable & Comparable<? extends Serializable & Comparable<? extends Comparable<?>>>
}被装箱到1
,Integer
被传递为"1"
。
没有泛型:
String
即使没有泛型,您也可以传递private static void method(Number arg1, Number arg2) {
和Integer
。
只有当问题类型为Double
时才能执行此操作,而不使用泛型:
final
我可以想到一个带有泛型的边缘情况,以确保它们是确切的类型。如果你有一个private static void method(String arg1, String arg2) {
// Yes, they're both Strings, guaranteed.
类,并且你设置了一个上限,那么你可以将它限制在同一个类中。
final
但是你可以在没有泛型的情况下做同样的事情。
public <T extends MyFinalClass> void method(T arg1, T arg2) {
// Yes, they're both MyFinalClasses
}
答案 1 :(得分:4)
您可以将该类添加为附加参数。
private static <T> void method(T arg1, T arg2, Class<T> type) {
// ...
}
现在你必须指定普通类型。
您仍然可以致电method(1, "1", Object.class);
,但至少您明确了常见类型。
答案 2 :(得分:1)
不可能这样做。或者换句话说,两个引用参数始终是“相同类型” - Object
- 引用类型的任何参数始终是Object
的实例。
T
始终可以是Object
,并且可以使用任意两个引用参数。即使使用<T, U extends T> void method(T arg1, U arg2)
,T
和U
也可以是Object
,因此可以再次使用任意两个参数。
这背后的根本原因是没有类型安全的理由来制定这样的约束。继承的一个主要问题是,应该可以安全地处理子类实例,如超类实例。超类类型的引用类型可以自由地指向该类或子类的实例。因此,具有相同编译时类型的两个引用变量始终可以完全安全地在运行时指向不同子类类型的实例。因此,在编译时,您永远不能对两个实例的实际运行时类之间的关系做出任何声明,而不是它们是编译时类型的子类。由于两个参数在运行时是不同类的实例是安全的,因此实际传递不同编译时类型的两个参数也同样安全。
答案 3 :(得分:0)
这就是我解决这个问题的方法 - 我想创建一个方法来创建两个对象的差异。
public List<Change> getChanges(Object o1, Object o2);
当然,我希望 o1 和 o2 属于同一类型。我通过将这个方法封装在一个参数化的类中来解决这个问题。
public class DiffGenerator<T> {
public List<Change> getChanges(T o1, T o2) {
//code
}
}
这可以用作:
List<Change> changes = new DiffGenerator<MyClass>().getChanges(myClassOld, myClassNew);
为我工作。