扁平化类型参数

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

class Foo {
    public  Foo foo(Foo a) {...}
}

class Bar {
    public  Bar foo(Bar a) {...}
}

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

  直接来说方法


interface IGeneric> {
     IGeneric foo(IGeneric val);
}

class Foo implements IGeneric> {
    public  Foo foo(IGeneric> val) {...}
}

class Bar implements IGeneric> {
    public  Bar foo(IGeneric> val) {...}
}

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

interface IExample {
    T foo();
}

class Foo implements IExample {
    Foo foo();
}

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

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

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

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

  附上Monad的示例

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

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

你可能感兴趣的:(java)