【问题标题】:Bounded types: Multiple bounds有界类型:多个边界
【发布时间】:2014-04-11 13:53:26
【问题描述】:

我已经阅读了这篇文章here 并试图弄清楚如何使用绑定类型。我试图实现的是一种处理四种不同情况的参数化方法:

  • T 仅扩展 B
  • T 扩展 B 和 I(此处为 D)
  • T 仅扩展 I
  • 其他一切

代码如下:

public class Main {

    public static void main(String... args) {
        B b = new B();
        D d = new D();
        I i = new I() {
        };
        handle("aaasd");
        handle(b);
        handle(d); <---- Problem 1
        handle(i);
    }

    public static class B {

    }

    public static interface I {

    }

    public static class D extends B implements I {

    }


    public static <T> void handle(T objT) {
        System.out.println("T");
    }

    private static <T extends B> void handle(T obj) {
        System.out.println("B");
    }

    public static <T extends B & I> void handle(T objT) { <--- Problem 2
        System.out.println("B+I");
    }

    private static <T extends I> void handle(T obj) {
        System.out.println("I");
    }
}

编译器抱怨并说了两件事:

  1. 模棱两可的称呼

    方法句柄(Main.D)对于 Main 类型不明确 我猜这个问题是由与问题 2 相同的原因引起的。& I 清楚地将 T 的类型限制为 B 的子类型,因此我认为消除了歧义。

  2. 相同的擦除句柄

    方法句柄(T)与 Main 类型中的另一个方法具有相同的擦除句柄(Main.B) 我的猜测是,这是所有问题的真正原因。 Java 在运行时以某种方式消除了对 I 的限制?但是当我调用 B 类型的方法时,这不会调用注释的方法。

有人可以解释我如何解决问题/区分 B、B&I 和 I 吗?

【问题讨论】:

  • 如果你想要最后的所有 3 个 handle 方法,你无法修复它。
  • 那么还有其他方法吗?

标签: java multiple-inheritance bounded-types


【解决方案1】:

Java 在运行时以某种方式移除了对 I 的边界?

不,Java 会在运行时删除 每个 类型信息(反射目的除外),这称为类型擦除。

使用边界,编译器可以将您的代码转换为handle(Object)handle(B)handle(I),但在T extends B &amp; I 的情况下,编译器会发生冲突。

AFAIK,如果没有共同的界限,就无法解决这个问题,例如T extends D 而不是 T extends B &amp; I where D extends B implements I 或更改方法名称或添加另一个参数。

另一种方法可能是将 B+I 案例中的逻辑添加到 B 或 I 方法并检查内部的第二个条件,例如

private static <T extends B> void handle(T obj) {
    if( obj instanceof I) { 
      System.out.println("B+I");
    }
    else {
      System.out.println("B");
    }
}

【讨论】:

  • Java 你为什么要对我这样做......不过还是谢谢。并且检查方法内部并不是一个很好的选择。用户可能会决定“将其视为B 而不是B &amp; I。我对通用绑定的问题是我想接受来自不具有这些通用类型的就地API 的对象。(也许是新的编码标准?总是创建一个充当通用类型的抽象类,有点像绑定组?)
  • @WorldSEnder 好吧,很多人认为 Java 泛型遗漏了很多东西,但总是存在向后兼容性问题,这使得它很难适合每个人。如果它是一个 API 并且用户应该能够决定调用哪一个,您可以只重命名该方法或只提供 handle(B)handle(I) 并且每当用户传入一个适用于两者的对象时,让调用者通过使用演员来决定。
【解决方案2】:

有一个称为类型擦除的概念适用于 Java 中的所有泛型。使用泛型方法,编译后,字节码中的方法会作为它们的擦除出现,所以

public static <T> void handle(T objT) {
    System.out.println("T");
}

private static <T extends B> void handle(T obj) {
    System.out.println("B");
}

public static <T extends B & I> void handle(T objT) { <--- Problem 2
    System.out.println("B+I");
}

private static <T extends I> void handle(T obj) {
    System.out.println("I");
}

实际上变成了

public static void handle(Object objT) {
    System.out.println("T");
}

private static void handle(B obj) {
    System.out.println("B");
}

public static void handle(B objT) { 
    System.out.println("B+I");
}

private static void handle(I obj) {
    System.out.println("I");
}

类型变量的最左边界是该类型的参数被替换的内容。如您所见,您的第二个和第三个方法都具有相同的名称和相同的参数类型,即。同一个签名。这是编译器不允许的。

但是,bounds 的语法会强制您在任何接口类型之前提供类类型

<T extends I & B>

行不通。它也不起作用,因为您的第 4 种方法将再次具有相同的擦除。

另外,调用

handle(d);

是一个问题,因为第二种和第 4 种方法都可以处理它,没有一个更具体。这被称为重载歧义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-31
    相关资源
    最近更新 更多