美文网首页
Java/Kotlin中泛型的递归定义

Java/Kotlin中泛型的递归定义

作者: Wannay | 来源:发表于2022-04-03 00:46 被阅读0次

    1. 递归定义泛型

    1.1 原始情况-问题由来

    当我们定义了如下的关系的类时:

    abstract class AbstractBuilder {
        private var id: Int? = null
    
        open fun setId(id: Int): AbstractBuilder {
            this.id = id
            return this
        }
    }
    
    open class MyBuilder1 : AbstractBuilder() {
        private var name: String? = null
    
        open fun setName(name: String): MyBuilder1 {
            this.name = name
            return this
        }
    }
    
    class MyBuilder2 : MyBuilder1() {
        private var age: Int? = null
    
        fun setAge(age: Int): MyBuilder2 {
            this.age = age
            return this
        }
    }
    

    我们想要依次完成setId/setName/setAge的链式调用,但是当我们完成了setId之后,发现完成不了setName和setAge了,因为setId返回的是AbstractBuilder类型,而不是它的子类类型。

    MyBuilder2().setId(1)
    

    如果我们想要完成链式调用,可以使用下面的代码

    ((MyBuilder2().setId(1) as MyBuilder2).setName("wanna") as MyBuilder2).setAge(18)
    

    但是这个过程中,使用到了多次强转。但是我们明明已经知道this其实是个MyBuilder2类型了呀,为啥还要我强转啊!!!

    原因在于,在父类当中根本无法知道子类的类型。这个很容易理解对吧,我怎么有哪些子类会存在?但是我们可以利用泛型去进行实现,我们通过泛型让子类告诉父类我是什么类型

    1.2 使用泛型去实现让父类知道子类的类型

    abstract class AbstractBuilder<B : AbstractBuilder<B>> {
        private var id: Int? = null
    
        open fun setId(id: Int): B {
            this.id = id
            return this as B
        }
    }
    
    open class MyBuilder1 : AbstractBuilder<MyBuilder1>() {
        private var name: String? = null
    
        open fun setName(name: String): MyBuilder1 {
            this.name = name
            return this
        }
    }
    
    class MyBuilder2 : MyBuilder1() {
        private var age: Int? = null
    
        fun setAge(age: Int): MyBuilder2 {
            this.age = age
            return this
        }
    }
    

    我们在AbstractBuilder当中定义了泛型B,而B又必须继承于AbstractBuilder<B>,在子类MyBuilder1当中,我们将泛型类型B设置为自己本身的类型,这样,在父类当中通过this as B,就可以拿到MyBuilder1类型了。

    但是当我们调用完setId和setName之后,因为不管AbstractBuilder中的setId方法还是MyBuilder1中setName方法返回的还是MyBuilder1类型,并不是MyBuilder2类型的。

    MyBuilder2().setId(1).setName("wanna")
    

    我们继续改造

    abstract class AbstractBuilder<B : AbstractBuilder<B>> {
        private var id: Int? = null
    
        open fun setId(id: Int): B {
            this.id = id
            return this as B
        }
    }
    
    open class MyBuilder1<B : MyBuilder1<B>> : AbstractBuilder<B>() {
        private var name: String? = null
    
        open fun setName(name: String): B {
            this.name = name
            return this as B
        }
    }
    
    class MyBuilder2 : MyBuilder1<MyBuilder2>() {
        private var age: Int? = null
    
        fun setAge(age: Int): MyBuilder2 {
            this.age = age
            return this
        }
    }
    

    我们让MyBuilder1也带上泛型B,并且约束泛型类型必须继承于MyBuilder1<B>,接着在MyBuilder2当中继承时,也带上MyBuilder2的泛型了。

    接着,我们就可以完成我们的需求,依次完成setId/setName/setAge。

    MyBuilder2().setId(1).setName("wanna").setAge(18)
    

    不管是setId/setName/setAge,返回的类型都会根据我们传递的泛型类型去推断。也就是说,当我们使用MyBuilder2去创建对象时,setId/setName/setAge返回的都是MyBuilder2类型。

    1.3 递归定义泛型的作用

    我们想让父类A知道子类B的类型,我们完全可以在父类当中定义泛型H : A<H>,在子类B当中去定义一个泛型H : B<H>,如果B再来一个子类C,我们可以继续去在C当中去定义泛型H : B<H>。实际上,在很多语言当中,我们管父类中匹配子类的类型称为self,父类返回父类自身称为this,但是在Kotlin和Java当中,都是没有提供self的特性的。

    有一个问题:为什么MyBuilder2当中继承父类的是<MyBuilder2>,而不是像之前说的,定义一个泛型H呢。因为MyBuilder2类本身就不是open的,也就是说这个类是final的,已经不允许子类去继承了,因此我们完全可以写死泛型类型。

    还有个问题,我们如果想要构造一个MyBuilder1,去完成setId和setName呢?需要怎么做,因为该类MyBuilder1的泛型,是需要我们去定义的,我们需要传递一个MyBuilder1的泛型进去。

    MyBuilder1<MyBuilder1>().setId(1).setName("wanna")
    

    但是,这样使用是不是感觉没有MyBuilder2那样简洁呢?在Kotlin1.6之后,支持将泛型抹掉,直接使用如下的方式去进行构建:

    MyBuilder1().setId(1).setName("wanna")
    

    Kotlin会直接支持泛型的类型匹配,因为我们在MyBuilder1的泛型当中定义了泛型B : MyBuilder1<B>。因此当我们省略泛型时,Kotlin能够自动对泛型进行匹配,得到MyBuilder1的泛型类型,从而可以让我们省略泛型可以不写。但是,在Kotlin1.6之前是并不支持这样的泛型推断的。、、

    实际上,Java当中完全同理

    abstract class AbstractBuilder<B extends AbstractBuilder<B>> {
    
        private int id;
    
        public B setId(int id) {
            this.id = id;
            return (B) this;
        }
    }
    
    class MyBuilder1<B extends MyBuilder1<B>> extends AbstractBuilder<B> {
    
        private String name;
    
        public B setName(String name) {
            this.name = name;
            return (B) this;
        }
    }
    
    final class MyBuilder2 extends MyBuilder1<MyBuilder2> {
    
        private int age;
    
        public MyBuilder2 setAge(int age) {
            this.age = age;
            return this;
        }
    }
    

    完全能够推断出来self类型,MyBuilder1也支持不指定泛型去进行创建对象。

            new MyBuilder2().setId(1).setName("wanna").setAge(18);
            new MyBuilder1<>().setId(1).setName("wanna");
    

    1.4 递归定义泛型的应用

    在Netty中,定义了如下的类

    AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel>
    

    而AbstractBootStrap的子类有如下两个

    ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel>
    Bootstrap extends AbstractBootstrap<Bootstrap, Channel>
    

    在我们使用中,会使用如下的方式去创建ServerBootStrap,并启动Netty程序。

            NioEventLoopGroup bossGroup = new NioEventLoopGroup(bossLoops);
            NioEventLoopGroup workerGroup = new NioEventLoopGroup(workerLoops);
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(initializer)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture future = serverBootstrap.bind(port);
    

    我们往往会用到option/childOptionhandler/childHandler等方法,但是带child的方法定义在子类,不带child的方法定义在父类,父类返回的确是子类类型的对象。

    在父类当中,handler方法的实现如下,它返回的就是self,实际上就是基于递归定义泛型的方式去进行实现的在父类当中,返回子类类型的对象。

        public B handler(ChannelHandler handler) {
            this.handler = ObjectUtil.checkNotNull(handler, "handler");
            return self();
        }
        
        private B self() {
            return (B) this;
        }
    

    相关文章

      网友评论

          本文标题:Java/Kotlin中泛型的递归定义

          本文链接:https://www.haomeiwen.com/subject/umoxsrtx.html