Kotlin安卓称王?教你用Java调用Kotlin高级特性!(建议收藏-程序员宅基地

技术标签: java  android  kotlin  开发语言  

作者:newki
链接:https://juejin.cn/post/714712698016759809

虽然 Kotlin 推出很多年了,但是在国内的普及度并没有成压倒性优势,还是有很多新老项目使用Java语言开发的。(Java永不为奴 )

如果项目中其他小伙伴使用的Kotlin,而我只会Java,那我怎么调用他Kotlin的方法?其实Kotlin早给我们做好了兼容,很多特性我们都可以使用Java来调用。

下面一起看看一些常用的Kotlin特性如何使用Java语言来调用。

1.Java调用KT属性与方法

Kotlin的属性与方法,在Java中的调用。这个大家应该都会,简单过一下。

我们在Kotlin类中定义一些基本的方法与变量:


class KotlinDemo {

    var name: String = "newki"

    fun printName() {
        YYLogUtils.w("name:$name")
    }

    val age: Int
        get() {
            return 28
        }

}

能点出来的方法:

f7f548c1023654bab97cb9bb28f24b5c.jpeg

2.Java调用KT静态属性与方法

静态的属性与方法,我们需要注意注解的使用。

我们在Kotlin类中定义一些静态的方法与变量:


class KotlinDemo {

    companion object {

        var school: String = "wuhandaxue"

        @JvmField
        var industry: String = "IT"

        fun callStaticMethod1() {
            YYLogUtils.w("调用静态方法")
        }

        @JvmStatic
        fun callStaticMethod2() {
            YYLogUtils.w("调用静态方法")
        }
    }
}

能点出来的方法:

99c39a2bdc533acf2e316cc2325cd9b3.jpeg

具体的使用:

KotlinDemo.Companion.callStaticMethod1();
KotlinDemo.callStaticMethod2();

KotlinDemo.Companion.getSchool();
KotlinDemo.Companion.setSchool("11");
KotlinDemo.industry = "xx";

3.Java调用KT顶层函数/扩展函数

Kotlin的顶层函数或者叫扩展函数,我们把函数的类名加上后缀kt即可直接调用,默认的扩展函数都是可以调用的,需要注意的是加上泛型的一些方法。

比如我们的Kotlin类中定义的顶层函数


fun topLevelFun() {
    YYLogUtils.w("调用顶层函数")
}

我们在基类中定义的一些扩展函数:CommonExt.kt:


fun Context.dp2px(dpValue: Float): Int {
    return (dpValue * resources.displayMetrics.density + 0.5f).toInt()
}

ActivityExt.kt:


inline fun <reified T> Context.gotoActivity(
    flag: Int = -1,
    bundle: Array<out Pair<String, Any?>>? = null
) {
    val intent = Intent(this, T::class.java).apply {
        if (flag != -1) {
            this.addFlags(flag)
        }
        if (this !is Activity) {
            this.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        if (bundle != null) {
            putExtras(bundle.toBundle()!!)
        }
    }
    startActivity(intent)
}

我们可用的一些扩展函数和一些不可用的扩展函数:

f5cfb495a18c78a601cbc6e94ecb7517.jpeg

可以看到 ActivityExt 类中的部分扩展函数不能使用。一些内联函数+标注泛型的扩展方法确是无法使用。(还我用的不对吗?不知道有没有大佬指点!)

4.Java调用KT高阶函数

高阶函数就是可以当参数方法函数,例如最简单的高阶函数对象 () -> Unit 这种函数在转成 Java 的过程会生成对应的 Function 接口。我们直接 new Function 的匿名接口对象即可。

比如我们定义一些高级函数的方法:


class KotlinDemo {

    fun String.setValueCallback(block: () -> Unit) {

        block()
    }

    fun setValueCallback2(block: () -> Unit, action: (Int) -> Int) {

        block()

        val action1 = action(1)
        YYLogUtils.w("我收到处理的值了-计算的结果:$action1")
    }

}

那么我在Java中怎么调用呢?

public void demo2(View view) {

        KotlinDemo demo = new KotlinDemo();

        demo.setValueCallback("str", new Function0<Unit>() {
            @Override
            public Unit invoke() {

                ToastUtils.INSTANCE.makeText(mActivity, "我被回调了!!");

                return null;
            }
        });

        demo.setValueCallback2(new Function0<Unit>() {
            @Override
            public Unit invoke() {

                YYLogUtils.w("这里没有返回值,我回调到我就行了");

                return null;
            }
        }, new Function1<Integer, Integer>() {
            @Override
            public Integer invoke(Integer integer) {

                YYLogUtils.w("这里有返回值,回调到这里,我还需要处理运算,我的上级才能收到我处理的值");

                return integer + 10;
            }
        });

    }
打印的结果:

0c7b7e6631bc2776160736a82d7cbc87.jpeg

高阶函数的Java调用可以说是后续的基础,关于 Function 的接口,从Function1 到 Function22 Kotlin给我们定义了20多个接口对象,只是用于区分有多少个参数,本质都是一样的。

de7f236dd00fdab703ea885f2fdafe9f.jpeg

不会真有人定义这么多参数吧,我最多用到三个参数

5.Java调用KT的SAM方式

其实Sam是基于Kotlin的简化方式,对应Java调用Kotlin来说其实是一样的,它只适应于Kotlin。

比如:

interface MyCustomCallback {

    fun onCallback()
}

fun interface MyCustomCallback {

    fun onCallback()
}
fun setSamMethod(callback: MyCustomCallback) {

    callback.onCallback()
}

在Java中是一样的调用

    
public void demo5(View view) {

    KotlinDemo demo = new KotlinDemo();

    demo.setSamMethod(new MyCustomCallback() {
        @Override
        public void onCallback() {
            YYLogUtils.w("回调到了");
        }
    });
}

只是在Kotlin语言中new的方式不同罢了:


fun setValueCallback2(block: MyCustomCallback.() -> Unit) {

    block(object : MyCustomCallback {
        override fun onCallback() {
            YYLogUtils.w("对方调用了,然后我来继续执行")
        }
    })

}
fun setValueCallback2(block: MyCustomCallback.() -> Unit) {

        block(MyCustomCallback { YYLogUtils.w("对方调用了,然后我来继续执行") })

    }

其实是和高阶函数的调用比较类似,只是一个是我们自己定义的接口,一个是高阶函数Java转换的Function接口,使用起来是一样的。

6.Java调用KT高阶扩展函数

什么叫高阶扩展函数,其实就高阶函数,只是把原本回调的对象类型放在了前面以扩展的方式表达。以扩展的方式定义高阶函数,我把它叫做高阶扩展函数,但其本质还是高阶函数。

举一个很简单的例子


fun String.setValueCallback11(block: Industry.(Int) -> Unit) {
    block(Industry(0, this, "123", "456"), 10)
}

fun String.setValueCallback12(block: (Industry, Int) -> Unit) {
    block(Industry(0, this, "123", "456"), 10)
}

前者是高阶扩展函数,后者是高阶函数,使用的方式是一样的,只是高阶函数回调的时候一个需要用it或者指定变量接收,而高阶扩展函数只需要this接收,并且高阶扩展函数默认在回调的第一个参数上。

例如:


"test".setValueCallback11 { int ->
    YYLogUtils.w("收到回调:industry:" + this.toString() + " int:" + int)
}

"test".setValueCallback12 { industry, int ->
    YYLogUtils.w("收到回调:industry:" + industry.toString() + " int:" + int)
}

一个直接用this,另一种需要用it或者自定义名称来接收。用起来都是一样的,只是使用高阶扩展函数的话,会有一些简写方式更加方便,(如果不懂的话可能会更懵吧)

如何定义高阶扩展函数

这种方式有几种定义方式,以函数的类型定义为扩展函数定义和非扩展函数定义。参数又分基本类型,自定义类型,与接口类型。

下面都会讲到,先从简单的常用的讲起。

基本数据类型:

    
//这种比较常见
fun String.setValueCallback(block: String.() -> Unit) {

    block(this.length.toString())
}

demo.setValueCallback("test", new Function1<String, Unit>() {
    @Override
    public Unit invoke(String s) {
        YYLogUtils.w("看看我收到的什么:" + s);
        return null;
    }
});

使用扩展方法再加上高阶扩展函数的参数,我们就可以直接使用this。就可以直接拿到数据设置回调。

这样的方式主要用于直接回调给对方

打印结果:

04f6bf0a09508c10bc3b9c30def7676e.jpeg

自定义对象类型:

如果我们修改高阶扩展函数的对象,你看这样就不协调了,String的扩展方法,确实回调的Industry对象。

fun String.setValueCallback(block: Industry.() -> Unit) {
    //直接回调给对方
    block(this.length.toString())
}

如果还是使用String的参数,上面的用法就会报错,我们应该回调Industry的对象。

    
fun String.setValueCallback(block: Industry.() -> Unit) {
    //直接回调给对方
    block(Industry(0, this, "123", "456"))
}

我们把String的扩展this当做Industry的构造参数的一个属性,这样才能回调一个Industry对象给Java

看Log打印,回调的就是Industry

ecd9a369f995847fd2fb450b6531987d.jpeg

接口类型:

除了用对象做高阶扩展函数,我们还能以接口的形式做高阶扩展函数。

   
//还能设置不相关的对象扩展
fun setValueCallback2(block: MyCustomCallback.() -> Unit) {

    block(object : MyCustomCallback {
        override fun onCallback() {

        }
    })

}

使用接口类型做我参数的扩展,这里我们需要new一个对象给调用者。就导致这种方法是 回调给对方调用再通过对方回调给自己

在Java的代码中我们可以直接调用接口的方法,让对方去执行

    
public void demo3(View view) {
    KotlinDemo demo = new KotlinDemo();

    demo.setValueCallback2(new Function1<MyCustomCallback, Unit>() {
        @Override
        public Unit invoke(MyCustomCallback myCustomCallback) {

            //调用
            myCustomCallback.onCallback();

            return null;
        }
    });
}

打印的Log如下:

d4f14cd88cf12adede9c7306a96e274a.jpeg

不管是用Java代码去调用,还是用Ktolin代码调用,本质是一样的。

需要注意方法的参数,是否是基本类型的高阶扩展函数,还是对象类型的高阶扩展函数,还是接口类型的高阶扩展函数。这会导致是回调的方式不同。

而方法本身是否是扩展函数,则相对没有那么重要,主要是看方便我们能否使用this,如果需要this对象就用扩展函数,如果不需要那我们可以不用。

关于高阶扩展函数的补充

其实前文也说了,本质还是高阶扩展函数,只是区分是否用this接收而已,所以高阶扩展函数也是针对Kotlin来说的,对Java语言来说,还是以高阶函数的方式来处理。

举例说明:


fun String.setValueCallback1(block: String.(Int) -> Unit) {
    block(this, 10)
}

Java代码中使用这一种高阶扩展函数:


KotlinDemoKt.setValueCallback1("test", new Function2<String, Integer, Unit>() {
     @Override
    public Unit invoke(String s, Integer integer) {
        YYLogUtils.w("收到回调:s:" + s + "integer:" + integer);
        return null;
    }
});

可以看到Java语言可不管你扩不扩展,对它来说就是两个参数,我也不能用this什么的,我只能用转换过的Function。可以看到Kotlin虽然定义的是Int,但是Java接收到的是 Function2 是两个参数的回调,顺序是第一个是String第二个是Int。

前文讲到的基本类型,对象类型,接口类型,这里做一个全部的Demo


fun String.setValueCallback0(block: (Int) -> Unit) {
    block(10)
}

fun String.setValueCallback1(block: String.(Int) -> Unit) {
    block(this, 10)
}

fun String.setValueCallback11(block: Industry.(Int) -> Unit) {
    block(Industry(0, this, "123", "456"), 10)
}

fun String.setValueCallback12(block: (Industry, Int) -> Unit) {
    block(Industry(0, this, "123", "456"), 10)
}

fun String.setValueCallback13(block: MyCustomCallback.(String) -> Unit) {

    block(MyCustomCallback { YYLogUtils.w("对方调用了,然后我来继续执行") }, this + "加点后缀")

}

为了方便理解,回调的时候我故意没有省略this,一般大家写代码都会省略this。

如果是Kotlin代码调用,很简单

        
"test".setValueCallback1 { int ->
    YYLogUtils.w("收到回调:str:" + this + " int:" + int)
}

"test".setValueCallback11 { int ->
    YYLogUtils.w("收到回调:industry:" + this.toString() + " int:" + int)
}

"test".setValueCallback12 { industry, int ->
    YYLogUtils.w("收到回调:industry:" + industry.toString() + " int:" + int)
}

"test".setValueCallback13 { str ->
    YYLogUtils.w("收到回调:callback:" + this + " str:" + str)
    this.onCallback()
}

打印如下:

b510a8b950e0e53cd897f21b29ab1a10.jpeg

如果是Java代码调用:

    
public void demo2(View view) {

    KotlinDemo demo = new KotlinDemo();

    KotlinDemoKt.setValueCallback1("test", new Function2<String, Integer, Unit>() {
        @Override
        public Unit invoke(String s, Integer integer) {
            YYLogUtils.w("收到回调:s:" + s + "integer:" + integer);
            return null;
        }
    });

    KotlinDemoKt.setValueCallback11("test", new Function2<Industry, Integer, Unit>() {
        @Override
        public Unit invoke(Industry industry, Integer integer) {
            YYLogUtils.w("收到回调:industry:" + this.toString() + " integer:" + integer);
            return null;

        }
    });

    KotlinDemoKt.setValueCallback12("test", new Function2<Industry, Integer, Unit>() {
        @Override
        public Unit invoke(Industry industry, Integer integer) {
            YYLogUtils.w("收到回调:industry:" + this.toString() + " integer:" + integer);
            return null;

        }
    });

    KotlinDemoKt.setValueCallback13("test", new Function2<MyCustomCallback, String, Unit>() {
        @Override
        public Unit invoke(MyCustomCallback myCustomCallback, String s) {

            YYLogUtils.w("收到回调:callback:" + myCustomCallback.toString() + " str:" + s);
            myCustomCallback.onCallback();

            return null;
        }
    });

可以看到这种方式对Java是没有效果的,和高阶函数的使用是一模一样的。

打印Log如下:

8c03ae35e773b3a86ddfe572d218116a.jpeg

结论:这种以高阶函数定义的扩展函数,对Java的调用来说没效果,只是Kotin语言的一些简化。

7.Java调用KT的DSL方式

按照顺序一步一步的来,我们会了Java调用扩展函数和高阶扩展函数,那么在此基础上我们就能完成Kotlin的DSL调用了。

我们都知道DSL的简化规则是基于高阶扩展函数间接实现的(也可以直接高阶函数实现,不过使用的时候就需要用it去点出方法,不够优雅,所以一般大家都用高阶扩展函数的方式去实现)。

那么我们学了Java调用高阶扩展函数之后,再回头看看DSL的调用有什么不同。

之前的文章已经讲过DSL定义的几种方式,这里不多BB,直接快速过一次代码:


fun TestNet.setOnSuccessCallbackDsl(init: SuccessCallbackImpl.() -> Unit) {
    val listener = SuccessCallbackImpl()
    init(listener)
    this.setOnSuccessCallback(listener)
}


var mCallback: SuccessCallback? = null

fun setOnSuccessCallback(callback: SuccessCallback) {
    this.mCallback = callback
}


interface SuccessCallback {  //多个参数不能使用fun修饰了
    fun onSuccess(str: String): String

    fun doSth()
}


class SuccessCallbackImpl : TestNet.SuccessCallback {

    private var onSuccess: ((String) -> String)? = null

    private var doSth: (() -> Unit)? = null

    fun onSuccess(method: (String) -> String) {
        onSuccess = method
    }

    fun doSth(method: () -> Unit) {
        doSth = method
    }

    override fun onSuccess(str: String): String {
        return onSuccess?.invoke(str).toString()
    }

    override fun doSth() {
        doSth?.invoke()
    }
}

我们定义一个测试方法用于回调数据

    
fun requestDSLCallback() {

    MainScope().launch {

        val result = withContext(Dispatchers.IO) {
            delay(500)

            return@withContext Random().nextInt(10)
        }

        val res = mCallback?.onSuccess("DSL测试-->成功")

        YYLogUtils.w("result:$res")

        mCallback?.doSth()


    }
}

Kotlin代码使用DSL,相信大家都已经轻车熟路了。

    
val testNet = TestNet()

testNet.setOnSuccessCallbackDsl {
    onSuccess { str ->
        YYLogUtils.w("str: $str")
        str + "再加一点数据"
    }
    doSth {
        YYLogUtils.w("可以随便写点什么成功之后的逻辑")
    }
}

testNet.requestDSLCallback()

打印Log:

71e5b51f166013a614ffffc20fd4eb47.jpeg

重点是Java中如何调用DSL的这种方式呢?我们学了Java调用扩展方法和Java调用高阶扩展函数,那么组合在一起就是这样:

    
TestNet testNet = new TestNet();

TestNetKt.setOnSuccessCallbackDsl(testNet, new Function1<SuccessCallbackImpl, Unit>() {
        @Override
        public Unit invoke(SuccessCallbackImpl successCallback) {

            successCallback.onSuccess(new Function1<String, String>() {
                @Override
                public String invoke(String s) {

                    YYLogUtils.w("str:" + s);

                    return s + "再加一点数据2";
                }
            });

            successCallback.doSth(new Function0<Unit>() {
                @Override
                public Unit invoke() {

                    YYLogUtils.w("可以随便写点什么成功之后的逻辑2");
                    return null;
                }
            });
            return null;
        }
    });

testNet.requestDSLCallback();

我们需要在回调的方法中使用接口对象,手动的实现回调方法,回调的具体方法则是扩展函数的Java转换函数 Function 接口。

如此即可实现Kotlin的DSL调用,打印Log如下:

8cad09f91e38f74d1df7f19e69de6dfd.jpeg

看起来相对麻烦,但是仔细一瞅,其实还是Java调用高阶函数的那一套,只是Kotlin语言使用高阶扩展函数,看起来比较简洁而已。

8.Java调用KT协程

其实我们在Java代码中是无法使用协程或者说不方便使用协程的,但是我们介绍完上面的几种方案之后,我们可以间接的实现协程。

例如我们使用一个Kotlin工具类,作为一个中间层,然后通过回调的方式可以启动协程,切换协程的操作。虽然相对麻烦但是毕竟能用了!

我们再Kotlin代码中定义协程

    
//协程
fun setValueCallback3(block: CoroutineScope.() -> Unit) {

    GlobalScope.launch(Dispatchers.IO) {

        block(this)
    }

}

如果是在Kotlin代码中不用说,肯定是很简单的


KotlinDemo().setValueCallback3 {

    YYLogUtils.w("在协程中执行?"+Thread.currentThread().name)
}

运行:

6e44726da530d1f5c3265fe6ae429ebe.jpeg

但是在Java代码中我们如何运行呢?Kotlin代码中使用高阶函数默认就是 {} 即可,而在Java代码中帮我们转换为了 Function 的接口,相当于和上面的高阶扩展函数一样的用法。

    
public void demo4(View view) {
    KotlinDemo demo = new KotlinDemo();

    demo.setValueCallback3(new Function1<CoroutineScope, Unit>() {
        @Override
        public Unit invoke(CoroutineScope coroutineScope) {

            YYLogUtils.w("在协程中执行?" + Thread.currentThread().getName());

            return null;
        }
    });
}

运行:

407febe080ee419782c675a087af5fd2.jpeg

如果想指明 suspend标记,也可以的

    
fun setValueCallback4(block: suspend CoroutineScope.() -> Unit) {

    GlobalScope.launch(Dispatchers.IO) {

        block.invoke(this)
    }

}

使用的方式是一样的:

demo.setValueCallback4(new Function2<CoroutineScope, Continuation<? super Unit>, Object>() {
     @Override
    public Object invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) {

        YYLogUtils.w("在协程中执行suspend?" + Thread.currentThread().getName());

        return null;
     }
});

讲到这里给大家说明一个小细节,需要注意的是Kotlin代码中回调的写法

    
//协程
fun setValueCallback3(block: CoroutineScope.() -> Unit) {

    GlobalScope.launch(Dispatchers.IO) {

        block(this)
    }

}

前文讲过高阶扩展函数的一些简写,注意这里的回调调用其实有很多写法,但是都是一些简写,标准回调是:

block(this)

有些简写使用

block()

block

其实都没毛病,只是简写而已,但是但是,这么用 Kotlin 语言去调用没毛病,但是在Java语言中就不会回调了。

所以如果你看到别人的代码一些高阶扩展函数的回调使用的是默认的block(this)回调,可能也不是别人不会一些简写,可能别人是想适配Java的调用呢?

其实也更推荐大家使用标准的回调 block(this) 或  block.invoke(this)。这样函数的调用与内部的参数更标准化,更加的简单明了,适配性也更好。

下面我们继续更复杂一点的启动协程与切换协程 (PS:我真的不想再写了,真的有哪个冤种会在Java里面写协程的吗...)

简单的说说把,我们使用一个中间层,Kotlin工具类中定义一些协程的使用,然后通过高阶函数回调,或者高阶扩展函数回调。

    
fun callLaunch(block: suspend CoroutineScope.() -> Unit) {

    GlobalScope.launch(Dispatchers.Main) {

        block.invoke(this)

    }

}

suspend fun callWithContext(block: () -> String): String {

    return withContext(Dispatchers.IO) {

        block()
    }

}

使用的时候:


demo.callLaunch(new Function2<CoroutineScope, Continuation<? super Unit>, Object>() {
        @Override
        public Object invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) {

            YYLogUtils.w("启动了协程===>做点什么好呢" + Thread.currentThread().getName());


            demo.callWithContext(new Function0<String>() {
                @Override
                public String invoke() {

                    YYLogUtils.w("切换线程看看" + Thread.currentThread().getName());

                    return null;
                }
            }, new Continuation<String>() {
                @NonNull
                @Override
                public CoroutineContext getContext() {
                    return coroutineScope.getCoroutineContext();
                }

                @Override
                public void resumeWith(@NonNull Object o) {
                }
            });

            return null;
        }
});

打印结果:

6ec6ebf5549fd25546a160037d317070.jpeg

总的来说在Java中使用协程真的是折磨,就算使用回调间接的调用也是折磨,并且居然还要自己手动管理协程上下文。究极折磨!

总结

总的来说Java调用Kotlin的场景,协程不太好用,扩展函数中内联泛型不能使用,其他的一些方式我们都能通过Java来调用Kotlin代码。

所以说不会Kotlin一样可以使用Kotlin的一些特性,虽然用 Java 调用 Kotlin 很麻烦并且一点都不优雅,但毕竟能用了,要什么自行车。

话说到这里了,我还是比较推荐大家学习Kotlin,使用Kotlin搭建项目,毕竟我们往后面看一点,Compose 必然会成为趋势...

好了话说回来,关于Java调用Kotlin都是我平常自己项目的一些使用,难免闭门造车,如果你有更好的方式,可以评论区讨论。

好了,本文的全部代码与Demo都已经开源。有兴趣可以看这里。项目会持续更新,大家可以关注一下。

https://gitee.com/newki123456/Kotlin-Room

如有讲解不到位或错漏的地方,希望同学们也可以指出交流。

如果感觉本文对你有一点点的启发,还望你能点赞支持一下,你的支持是我最大的动力。

Ok,这一期就此完结。

关注我获取更多知识或者投稿

837e1fe7589996e4b5f4f2127943521b.jpeg

9ba7c02d38f0762418a285eae2698df2.jpeg

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/c6E5UlI1N/article/details/130537787

智能推荐

图像处理ITK与Visual Studio2017的配置教程!_itk和vs版本对应-程序员宅基地

文章浏览阅读2.9k次,点赞10次,收藏26次。VS2017配置ITK本次教程中的环境配置如下:Visual Studio2017,cmake 3.9.4,ITK 4.13;配置之前,声明一下,这里的VS选择2017,不是2019的原因,是源于2019版本太新,编译总是出现错误(之前测试过两次),所以在这里建议大家选择 VS版本的时候,最好在是2017及以前,配置之前,你需要确保下面三个软件已经安装好,或者已经下载完毕;Visua..._itk和vs版本对应

PHPStorm好用插件推荐之GitToolBox_phpstorm gittoolbox-程序员宅基地

文章浏览阅读2.4k次。1,GitToolBox,先上图。2,作用:显示当前代码提交的用户名、时间、以及备注信息_phpstorm gittoolbox

jungle rode_audiojungle声音模型-程序员宅基地

文章浏览阅读184次。The Head Elder of the tropical island of Lagrishan has a problem. A burst of foreign aid money was spent on extra roads between villages some years ago. But the jungle overtakes roads relentlessl..._audiojungle声音模型

python 自定义日历控件开发_self.configure(style='datepicker')-程序员宅基地

文章浏览阅读924次。学习python期间,发现 tkinter没有自带的日期选择控件。决定自己的写一个日期控件,费尽周转,终于写了一个自己满意的日期控件。本着人人为我,我为人人的原则,欢迎大家转发,评论,及提出宝贵的建议和意见。严重反感复制别人作品来获取下载积分和关注等行为。1.创建一个自定义DatePicker类,实现下拉日期选择。_self.configure(style='datepicker')

IntelliJ IDEA中配置Tomcat_idea配置tomcat-程序员宅基地

文章浏览阅读605次,点赞13次,收藏10次。InteliJ IDEA 配置Tomcat服务器_idea配置tomcat

python入门指南txt-【杂谈】爬虫基础与快速入门指南-程序员宅基地

文章浏览阅读168次。今天给大家分享一下网络爬虫的基础知识,以及一些优秀的开源爬虫项目。网络爬虫主要是我们在面对新的任务,但自己又没有数据的时候,获取自己想要的数据的一种手段。因此我们有必要掌握一定的爬虫知识,从而更好的准备训练数据集。作者 | 言有三编辑 | 言有三1 前端网页基础在介绍爬虫之前我们先介绍一下网页基础,理解前端网页有利于我们对后面爬虫的学习,它是爬虫的基础之一。1.网页构成通常来说网页由三部分组成,分..._python入门指南 小说 txt

随便推点

linux上下载nginx_nginx/1.21.5 rpm-程序员宅基地

文章浏览阅读252次。[ yum的代理服务器配置 ]如果想让CentOS中的yum可以通过代理服务器更新程序,则需要修改文件/etc/yum.conf,在此文件中加上:proxy=http://abcde:[email protected]:8080现在使用yum就可以使用了。CentOS6下yum安装 Nginx。1.在/etc/yum.repos.d/目录下创建一个源配置文件nginx.r..._nginx/1.21.5 rpm

numpy中的np.c_和np.r_详解_np.r_(newcols,data)-程序员宅基地

文章浏览阅读1.7k次。【时间】2018.12.03【题目】numpy中的np.c_和np.r_详解 一、np.c_和np.r_用于连接两个矩阵np.r_中的r是row(行)的缩写,是按行叠加两个矩阵的意思,也可以说是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等,类似于pandas中的concat()。n..._np.r_(newcols,data)

【机器学习】决策树(理论)_决策树理论-程序员宅基地

文章浏览阅读1w次,点赞63次,收藏324次。决策树(Decision Tree)是一种分类和回归方法,是基于各种情况发生的所需条件构成决策树,以实现期望最大化的一种图解法。由于这种决策分支画成图形很像一棵树的枝干,故称决策树。它的运行机制非常通俗易懂,因此被誉为机器学习中,最“友好”的算法。决策树由结点和有向边组成。结点有两种类型:内部结点(圆)和叶结点(矩形)。其中,内部结点表示一个特征(属性);叶结点表示一个类别。而有向边则对应其所属内部结点的可选项(属性的取值范围)。_决策树理论

Kubernetes之network: failed to set bridge addr: “cni0“ already has an IP address different from xxx问题_network: failed to set bridge addr: "cni0" already-程序员宅基地

文章浏览阅读818次,点赞2次,收藏3次。1 问题在使用Kubernetes部署应用时发现有Pod一直不能创建成功,使用kubectl describe pods <pod-name> -n <namespace>得到的结果如下图:从上面的截图中看到,问题出现在给Pod分配IP上,意思是cni0的IP不同于10.244.5.1/24,下面我们进入到knode1中使用ifconfig命令查看IP信息,结果如下:从上图中我们可以看到flannel.1的IP为10.244.5.0,然后使用cat /run/fl_network: failed to set bridge addr: "cni0" already has an ip address differe

Kubernetes 服务发布方式(蓝绿发布、灰度发布和滚动发布)-程序员宅基地

文章浏览阅读2.4k次,点赞3次,收藏10次。应用程序升级面临最大挑战是新旧业务切换,将软件从测试的最后阶段带到生产环境,同时要保证系统不间断提供服务。长期以来,业务升级渐渐形成了几个发布策略:蓝绿发布、灰度发布和滚动发布,目的是尽可能避免因发布导致的流量丢失或服务不可用问题。_蓝绿发布

西瓜视频 iOS 播放器技术重构-程序员宅基地

文章浏览阅读3.7k次,点赞3次,收藏8次。动手点关注干货不迷路????播放器简介播放器是西瓜视频等视频类 App 最主要的业务场景,也是最主要的流量入口,其承载包括下层基础播放,上层的各种播放业务:状态栏、弹幕、音量、亮度、评论、点赞、进度、倍速、清晰度、选集、合集、商业化等。西瓜对整个业务播放器做了整体抽象,提供了一套可插拔,可复用的播放器业务框架,包括:视频播放、播控交互、业务拓展。本文播放器是指业务播放器,主..._视频播放软件架构