【问题标题】:Java generics confusion with two generic super-typesJava 泛型与两个泛型超类型混淆
【发布时间】:2017-07-01 07:10:37
【问题描述】:

我有一个(穷人的)Java 中的两种类型:

public final class Either<L, R> {

    final boolean isLeft;
    final L l;
    final R r;

    private Either(final boolean isLeft, final L l, final R r) {
        this.isLeft = isLeft;
        this.l = l;
        this.r = r;
    }

    // map, right-projection, left-projection, etc.  

    public static <L, R> Either<L, R> left(final L l) {
        return new Either(true, l, null);
    }

    public static <L, R> Either<L, R> right(final R r) {
        return new Either(false, null, r);
    }
}

我想添加一个方法来将 L 或 R 连接到某种类型的实例 T

    public <T extends L & R> T join() {
        return isLeft ? (T)l : (T)r;
    }

但 Java 不喜欢这种通用约束:T extends L &amp; R。如何在 Java 类型系统中表示此操作?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    您在此处对T 施加的约束不起作用。编译器应该如何知道您放入到您的 Either 对象中的 L l 或 R r 同时是 L 和 R?!

    换句话说:您希望join() 的结果是Fruit;这将需要“传入” L l 和 R r 在您创建该对象时两个 Fruits 所在的位置。但是,当您没有将苹果和香蕉放入您的 Either 中,而是放入 Apple 和 Scissor 时会发生什么?你觉得你怎么能在一个共同的超类型下加入他们?!

    【讨论】:

    • 用户必须使用投影/地图方法将Scissor 转换为Fruit。一旦双方都继承自Fruit,那么就可以将它们连接起来得到最终的结果。
    • 如何将剪刀变成水果?它不是水果。
    • 一个更具体的例子是将Either&lt;Error, Result&gt;转换为面向用户的消息String
    【解决方案2】:

    您可能想要描述T 扩展L R 的情况(因为它显然不会同时扩展它们)。但是你不能在 Java 中做这样的规范。

    不要把这种情况复杂化,直接做:

    public <T> T join() {
        return isLeft ? (T)l : (T)r;
    }
    

    【讨论】:

    • 这行得通。确实需要用户使用正确的类型。显然,这不提供编译时类型安全性。
    • 确实如此,但在某些情况下您无法避免编译时类型不安全。
    • 参见Andy Turner's answer - 在这种情况下,可以让编译器做更多的工作。
    • @BoristheSpider 我看到了,它看起来让我很困惑。如果Either&lt;String, Integer&gt; 我的情况怎么办?
    • 然后你得到ObjectEither 唯一真正的用例是消费代码可以平等对待 either 响应,因此 Either&lt;String, Integer 没有真正意义。一个Either&lt;Result, Error&gt;,其中extends UserRespose 都可以正常工作。
    【解决方案3】:

    您只能在Either 上执行此操作,其中两种类型与您要返回的类型相关。由于通常情况并非如此,因此您需要在静态方法中执行此操作(或者,至少,某些方法不在Either 的实例上):

    static <T> T join(Either<? extends T, ? extends T> either) {
      return either.isLeft ? either.l : either.r;
    }
    

    【讨论】:

    • 这很聪明。由于 Java 8 的垃圾类型推断稍微少了一点,它可能会做一些有用的事情,而不仅仅是返回 Object
    • @BoristheSpider 不太清楚你的意思。这肯定比在 Java 7 中返回 Object 做得更好,而且我也会想到更早的版本。
    • 不确定 - 我会认为即使在 Java 7 中,在许多情况下它也需要泛型类型提示,尤其是链接到一系列方法调用中。
    • TBH 我不确定这个决定的想法是什么。如果我想这样做,这将不起作用:Either&lt;String, Long&gt; either = Either.left("dss"); Integer join = Either.join(either);
    • @Andremoniy 这个想法是它将选择较低的公分母。所以Either&lt;Long, Integer&gt; 将返回Number。当类型相关时,这将很有用 - 我想这是 Either 的唯一实际用例,因为任何一个结果都需要被同等对待。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 1970-01-01
    • 2011-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多