首页 > 编程知识 正文

扁平化特征,扁平化对应的是什么

时间:2023-05-06 07:59:27 阅读:255988 作者:222

语言: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)}

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。