如何在Java中编写一个通用方法,该方法接受两个相同类型的参数?

10

当我注意到以下代码可以编译并且输出 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> 无法编译。这是死路吗?

在调用方法时可以声明泛型类型:GenericsTest.<String>method(1, "1"); //编译失败 - August
4个回答

7
这段代码能编译成功是因为Java会推断出传入参数的最特定的超类型,这种情况下是ObjectSerializable & 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
}

但是你可以不使用泛型来做同样的事情。

public void method(MyFinalClass arg1, MyFinalClass arg2) {
    // Yes, they're both MyFinalClasses
}

在这种情况下,Java实际上推断出了超类型Serializable&Comparable <? extends Serializable&Comparable <? extends Comparable <?>>> - August

4
您可以将该类作为附加参数添加。
private static <T> void method(T arg1, T arg2, Class<T> type) {
    // ...
}

现在你需要指定通用类型。

你仍然可以调用 method(1, "1", Object.class); 但至少你要明确通用类型。


1
这是我解决问题的方法 - 我想创建一个方法,用于创建两个对象之间的差异 diff。 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); 对我很有用。

1
这是不可能的。或者换个角度看,两个引用参数总是“相同类型”——Object——任何引用类型参数都是Object的实例。 T总是可以是Object,并且接受任何两个引用参数。即使使用<T, U extends T> void method(T arg1, U arg2)TU都可以是Object,因此再次接受任何两个参数。
其根本原因在于没有类型安全的理由来限制这样做。继承的主要点之一是应该能够安全地将子类实例视为超类实例。超类类型的引用类型可以自由地指向该类或子类的实例。因此,具有相同编译时类型的两个引用变量始终可以安全地指向运行时的不同子类类型的实例。因此,在编译时,您永远不能对两个实例的实际运行时类关系作出任何陈述,除了它们是编译时类型的子类。由于在运行时两个参数是不同类的实例是安全的,因此传递两个不同编译时类型的参数也不会更不安全。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接