扁平化类型参数

语言:java
一个奇怪的要求:

class Foo<A> {
    public <B> Foo<B> foo(Foo<A> a) {...}
}

class Bar<A> {
    public <B> Bar<B> foo(Bar<A> a) {...}
}

  要求为Foo和Bar抽出一个接口
  这个来源于想要在Kotlin里实现Functor,Applicative和Monad的接口,却发现很难写

  直接来说方法


interface IGeneric<ValType, ClassType extends IGeneric<?, ClassType>> {
    <R> IGeneric<R, ClassType> foo(IGeneric<ValType, ClassType> val);
}

class Foo<A> implements IGeneric<A, Foo<?>> {
    public <R> Foo<R> foo(IGeneric<A, Foo<?>> val) {...}
}

class Bar<A> implements IGeneric<A, Bar<?>> {
    public <R> Bar<R> foo(IGeneric<A, Bar<?>> val) {...}
}

  首先一个常规技巧,接口方法要求子类方法返回子类

interface IExample<T extends IExample> {
    T foo();
}

class Foo implements IExample<Foo> {
    Foo foo();
}

但是这里我们不能这么干,因为这样会变成:

class Foo<T> implements IExample<Foo<T>> {
    Foo<T> foo(...);
}

  返回值的类型参数不应该是T
  而如果写成implements IExample<Foo<*>>,返回值就是Foo<?>,调用者需要强转。或者返回类型改为Foo<R>(协变),在实现内部强转,还是会警告,不优雅

  所以思路就是把子类中的类型参数拿出来,接口的类型参数中既有值的类型参数,又有子类的类型参数。所以叫扁平化类型参数。
  这样一来,子类实现时实际上的签名是public <R> IGeneric<R, Foo<?>> foo(IGeneric<A, Foo<?>> val)。返回类型协变一下,就可以变成Foo<R>了。
  一般来说会有这种要求的接口,只是为了给子类加一些约束而不是为了依赖接口(其实也没差啦),所以会直接使用子类。这样一来就不用调用方强转了。而在实现里强转也很神秘地不会报警告。可以亲自验证一下。(但其实这个强转并不保证成功才对,因为完全可以写class Bar implements IGeneric<A, Foo<?>>。完全不知道为什么没警告)

  附上Monad的示例

interface Monad<ValType, MonadType : Monad<*, MonadType>> {
    fun <B> flatMap(func: (ValType) -> Monad<B, MonadType>): Monad<B, MonadType>
}

class Maybe<T>(val value: T?) : Monad<T, Maybe<*>>, GenericClass<T> {
    override fun <R> flatMap(func: (T) -> Monad<R, Maybe<*>>): Maybe<R> =
            Maybe((value?.let(func) as? Maybe<R>)?.value)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值