约束接口实现

And*_*ács 13 java haskell interface

在Haskell(和Rust等)中,我可以拥有受其他实例约束的实例:

data Pair a b = Pair a b

instance (Eq a, Eq b) => Eq (Pair a b) where 
    Pair a b == Pair a' b' = a == a' && b == b'
Run Code Online (Sandbox Code Playgroud)

使用Java接口我不能.我必须要求类型参数Pair始终实现Eq,否则我根本无法实现Eq<Pair<A, B>>:

interface Eq<A> {
    public boolean eq(A other);
}

class Pair<A extends Eq<A>, B extends Eq<B>> implements Eq<Pair<A, B>> {
    A a;
    B b;
    public boolean eq(Pair<A, B> other){
        return a.eq(other.a) && b.eq(other.b);
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望有类似的东西:

class Pair<A, B> implements Eq<Pair<A, B>> if (A implements Eq<A> && B implements Eq<B>) {...}
Run Code Online (Sandbox Code Playgroud)

到目前为止,互联网告诉我,Java中没有直接支持我所需的功能.尽管如此,我发现这是接口(重新)可用性的一个相当关键的因素.我想知道是否有大致覆盖相同基础的变通方法或解决方案.

Pet*_*lák 7

我想到的一个规范解决方案是让比较器在类外部.这是Scala采用的方法,同时借助implicits使其易于消化.他们你有建立比较器的方法,比如

public <A, B> Comparator<Pair<A,B>> pairCmp(Comparator<A> ca, Comparator<B> cb) { ...
Run Code Online (Sandbox Code Playgroud)

然而,使用起来非常麻烦.Haskell在内部做同样的事情,传递类型类实现的字典,但类型类接口和类型推断使它更愉快.

据我所知,在Java中,如何声明条件实例是没有办法的.但更为面向对象的方法是为允许相等的对提供子类:

class PairEq<A extends Eq<A>, B extends Eq<B>>
  extends Pair<A,B>
  implements Eq<Pair<A, B>> {

...
Run Code Online (Sandbox Code Playgroud)

还需要一些手动过程,因为您需要决定何时使用Pair以及何时使用PairEq.但是通过方法重载,我们可以通过声明智能构造函数来使其更容易使用.由于Java总是选择最具体的一个,每当我们需要创建一对时,我们只是使用,如果参数适当地实现mkPair,Java将选择返回的一个:PairEqEq

public static <A,B> Pair<A,B> mkPair(A a, B b) {
    return new Pair<A,B>(a, b);
}

public static <A extends Eq<A>, B extends Eq<B>> PairEq<A,B> mkPair(A a, B b) {
    return new PairEq<A,B>(a, b);
}
Run Code Online (Sandbox Code Playgroud)

完整示例代码:

interface Eq<A> {
    public boolean eq(A other);
}

public class Pair<A,B> {
    public final A a;
    public final B b;

    public Pair(A a, B b) {
        this.a = a;
        this.b = b;
    }

    public static <A,B> Pair<A,B> mkPair(A a, B b) {
        return new Pair<A,B>(a, b);
    }

    public static <A extends Eq<A>, B extends Eq<B>> PairEq<A,B> mkPair(A a, B b) {
        return new PairEq<A,B>(a, b);
    }
}

class PairEq<A extends Eq<A>, B extends Eq<B>>
    extends Pair<A,B>
    implements Eq<Pair<A,B>>
{
    public PairEq(A a, B b) {
        super(a, b);
    }

    @Override
    public boolean eq(Pair<A,B> that) {
        return a.eq(that.a) && b.eq(that.b);
    }
}
Run Code Online (Sandbox Code Playgroud)


Zho*_*gYu 2

嗯...不。

但是我们可以有一个方便的方法,将 a “转换”Pair<A,B>为 an Eq<Pair<A,B>>,如果A,B两者Eq也是的话。这需要在使用现场执行额外的步骤,并且程序员必须在需要时进行显式类型转换;但这可能还不错

class Pair<A, B>
{
    A a;
    B b;
}

static
<A extends Eq<A>, B extends Eq<B>>
Eq<Pair<A,B>> cast(Pair<A,B> p1)
{
    return p2->( p1.a.eq(p2.a) && p1.b.eq(p2.b) );
}

--

    Pair<X,Y> pair = ...;

    Eq<Pair<X,Y>> eq = cast(pair);
Run Code Online (Sandbox Code Playgroud)

最好,我们希望将强制转换方法作为实例方法,例如

    Eq<Pair<X,Y>> eq = pair.asEq();
Run Code Online (Sandbox Code Playgroud)

然而,我们不希望它编译,除非X,Y满足约束。强制执行的一种方法

class Pair<A, B>
{
    A a;
    B b;

    <A2 extends Eq<A>, B2 extends Eq<B>>
    Eq<Pair<A2,B2>>
    asEq()
    {
        return p2->( p2.a.eq(a) && p2.b.eq(b) );
    }
}
Run Code Online (Sandbox Code Playgroud)