Giter Club home page Giter Club logo

developer-blog's People

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar

developer-blog's Issues

[kotlin]面向对象之接口、抽象类、伴生对象(Companion)

kotlin接口

开头我们介绍下kotlin的接口,kotlin接口跟java的接口差不多,jdk8中接口增加了默认方法实现,是为了兼容jdk向下兼容,还有考虑stream少写了很多代码,在kotlin中是否也可以写具体的方法,答案是肯定的。在多实现继承中有这样的问题,接口A有method方法的实现,父类B也有method方法实现,子类C怎么办,子类C必须override相同的方法签名,还有一个问题,怎样在子类C中指定调用A的实现,还有B的实现,这里kotlin也考虑的十分充分😆

interface A {
    fun method() {
        println("A")
    }
}

open class B{
    open fun method(){
        println("B")
    }
}

/**
 * 接口,父类中都有方法实现,子类必须自己实现一套
 */
class C: A, B(){
    override fun method() {
        //语法太风*
        super<A>.method()
        super<B>.method()
        println("C")
    }
}

fun main(args: Array<String>) {
    var c = C()
    c.method()
}

kotlin抽象类

kotlin抽象类没什么好说的,只有一点,父类BaseClass中有method方法实现,子类ChildClass可以把父类的实现变成抽象方法

//抽象类
open class BaseClass{
    open fun method() {
    }
}

/**
 * 可以用abstract方法override父类
 */
abstract class ChildClass : BaseClass() {
    override abstract fun method()
}

kotlin伴生对象(Companion)

伴生对象的概念是什么,可以用java语言方式理解,它是静态内部类,为什么要设计这样的伴生对象?
首先我们了解对象声明的概念,java中没有提供直接对象,但是js中提供了对象,面向对象是靠对象来实现的,即使ts语法糖里有class关键字,但是本质还是对象,对象就可以不用实例化,直接调用。

//object declaration,对象声明
object MyObject {
    fun method() {
        println("method")
    }
}
fun main(args: Array<String>) {
    MyObject.method()
}

下面来说明kotlin的伴生对象,下面的例子就是伴生对象的使用例子

class MyTest {
    companion object MyObject{
        var a: Int = 100

//        @JvmStatic
        fun method() {
            println("method invoked!")
        }
    }
}
fun main(args: Array<String>) {
    MyTest.MyObject.method()

    println("------")
    println(MyTest.a)
    MyTest.method() //类似于静态方法,Kotlin中没有静态方法
    println("------")
    val v = MyTest.MyObject
    println(v.javaClass)
}

伴生对象是为了提供给kotlin像java一样,可以使用一个静态的使用方式,《effetive java》 中static是特别推荐用的,有很多好处,初始化的次数少,可以做缓存,推荐使用静态工厂方法来构造一个类的实例。

  • 🚩kotlin中,与Java不同的是, 类是没有static方法的
  • 在大多数情况下,kotlin推荐的做法是使用包级别的函数来作为静态方法,kotlin会将包级别的函数当做静态方法来看待
  • 如果不提供伴生对象的名字,那么编译器会提供一个默认的名字Companion,MyObject描述可以省略
  • 🚩虽然伴生对象的成员看起来像是Java中的静态成员,但在运行期,他们依旧是真实对象的实例成员(调用方式是kotlin的特定语法糖)
  • 在jvm上,可以降版式对象的成员真正成为类的静态方法与属性,他们通过@JvmStatic注解来实现
  • 伴生对象在编译后会生成一个静态的内部类
    针对上面的tips,我们使用java反编译kotlin的class文件,开看一看伴生对象本质是什么
    1.我们不使用@JvmStatic注解,执行javap MyTest.class

image
如上图看,根据我们调用方式虽然可以 使用MyTest.method(),但是MyTest反编译结果并没有method静态方法
2.我们加上@JvmStatic,然后重新build
image
如图就是对每条tips的最好的证明😝

[node] 浅谈node异步编程

异步编程的痛点

还记得刚开始写前端的时候,如果我们按照顺序执行,牺牲代码质量,比如模块化,代码可重用,还有可维护,下面列举一个例子,这个例子看完之后有种似曾相似的感觉
0f644176-7ca9-4706-af1f-14bd3afce59a
这个问题叫做callback hell

callback discipline

有哪些规约可以减少上面的痛点呢

  • 你需要及时退出程序使用 return continue break ,而不是使用if/else
  • 命名callback函数成外面的clousure,传递参数给callback,
    命名好我们的函数会对栈追踪起到更好的效果(stack trace)
  • 我们需要模块化我们的代码,使其可用,切分更小
    重构后的代码

5ad3fd07-ff07-45ca-a22c-6748fc9e0860

fc935663-c337-4878-b604-16866306e85a

d47096da-a72d-41d9-ad1e-b202c4296b10

c1eba384-9043-498e-a906-ed62165ea315

[kotlin]第一个kotlin程序 编译器与反编译器

实现第一个程序,我们不需要编辑器
直接vim

  1. 安装kotlin 编译器,mac电脑下brew install kotlin whitch kotlinc 确认编译器安装成功
  2. vim 编写如下代码 不像java,最外层的是类,而是一个fun

df7d33ef-5461-47ae-b738-5a6717d9a094

这让人联想到js ,但是实时可以通过反编译开看生成了什么 先编译生成kotlinc HelloKotlin.kt ,会生成

3cb554ae-64af-4139-89ab-a75a7c2f1500

反编译HelloKotlinKt.class

dcbe95bb-42d6-45bb-9e96-e532d6799400

实际上编译器帮我们生成了按照jvm规范的类包含一个main函数 3. 我们也可以打包生成jar包,然后可以运行jar包kotlinc HelloKotlin.kt -include-runtime -d HelloKotlin.jar

3c184a05-9d3d-4b7a-95e3-d2968ac557d1

4acd3c6e-08c7-43d7-ac6e-47564f08b471

结果如上图

[kotlin]面向对象之继承,重载(跟java的设计很不一样)

我一直在想如何写好这一篇文章,因为此章节应该比之前面的章节更有价值,内容更加有趣,学习的其中一个乐趣在于看完一个理论,又在另外一个地方看到了理论的实践,随着开发经验的增加,发现技术并不是井喷式的增加,而是破冰式的,怎么说呢,技术在某个地方都有着相似的地方,看完koltin的继承,重载设计,还有很多关键字如 open ,override, final就不得不佩服koltin的设计👍,因为它在很多程度上遵守了《effective java》里面的items,下面容我娓娓道来😝。

// 在kotlin中, 所有类在默认情况下都是无法被继承的,
// 在kotlin中,所有类默认情况都是final的
// open的含义与final相反
open class Parent(name: String, age: Int){

}

class Child(name: String, age:Int): Parent(name, age) {

}

open class Parent2(name: String){

}


/**
 * 在kotlin中,如果一个类没有primary构造方法,那么这个类的secondary构造方法就需要通过
 * super关键字来初始化父类型,或者通过其他secondary 构造方法完成这个任务
 * 不同的secondary构造方法可以调用父类型的不同的构造方法
 */
class Child2: Parent2{
    constructor(name: String): super(name){
    }
}
  • 在kotlin中,所有类在默认情况下都是无法被继承的
  • 在kotlin中,所有类默认情况都是final的,field, fun都是final的👍
  • open关键字跟final关键字想法
    死记硬背这些条约其实没有意思,真正的理解背后设计的意义
    effective java中item15 minimize the accessibility of classes and members
    约束条款阐述如下
    1.好的组件设计是不对外暴露实现细节(另外的组件只能通过api来访问)
    2.一个好的组件需要隐藏自己的细节,而提供一个简洁的对外API
    3.公共的类中不要提供公共的field.保证public final 的成员是immutable的
    item17 minimize mutability
    优点这里不做阐述,java在这方便做的不好,我们喜欢用继承,但是用好继承的人很少,反而会造成系统的bug,程序员们不知道怎么样去安全的继承一个类,父类的设计,我们更倾向于安全的设计,所以koltin默认是final的,所有的内容不加open都是final,不推荐子类中使用,这样也很直观
    上面例子最后阐述了子类在没有primary构造函数继承的时候如何通过secondary构造方法直接或者间接完成任务
    下面在看一个例子,验证文字描述
open class Fruit{
    open fun name(){
        println("fruit")
    }

    fun expirationDate(){
        println("1 month")
    }
}

class Apple: Fruit(){
    override fun name(){
        println("apple")
    }
}

open class Orange: Fruit(){
    //orange name不可以被子类改写的
    final override fun name() {
        println("orange")
    }
}

fun main(args: Array<String>) {
    var apple = Apple()
    apple.name()
    apple.expirationDate()
}

open class MyParent{
    open val name: String = "parent"
}

class MyChild : MyParent() {
    override val name: String = "child"
}

fun main(args: Array<String>) {
    var myChild = MyChild()
    println(myChild.name)

    println("----")

    var myChild3 = MyChild3()
    myChild3.method()
    println(myChild3.name)
}

class MyChild2(override val name: String) : MyParent() {

}

open class MyParent3{
    open fun method() {
        println("parent method")
    }


    open val name: String get()= "parent"
}

/**
 * 1.val 可以 override val
 * 2.var 可以 override val
 * 2.val 不可以 override var
 * 本质上 val相当于get方法, var相当于get和set方法
  */
class MyChild3: MyParent3(){
    override fun method() {
        super.method()

        println("child method")
    }

    override val name: String
        get() = super.name + "and child"
 }

这里var修饰的 get()方法,不能被val修饰的成员变量重载,原因是,子类不能缩小父类的读写权限

effective java对于后端程序员如圣经般的存在,需要好好研读:innocent:

[kotlin] range的用法,list的用法,面向对象特性1

结合上篇文章,kotlin里面的range使用..来表示,java中的range一般是前闭后开,kotlin中的则不一样,2..8是表示2到8,也包含8,首先我们看个例子

    val a = 5
    val b = 10

    if (a in 2..b) {
        println("in the range")
    }

    if (a !in 2..b) {
        println("out of the range")
    }

    println("------")

    for (i in 2..10) {
        println(i)
    }

    println("------")

    for (i in 2.rangeTo(10)) {
        println(i)
    }

    println("------")

    //步进
    for (i in 2..10 step 2) {
        println(i)
    }

    println("------")

    //10-2 步进4
    for (i in 10 downTo 2 step 4) {
        println(i)
    }

第一个跟第二循环表示的意思相同,第三个通过step来进行步进,第四个则是反过来步进,很直观

java8中stream操作一个数组很方便,那么在kotlin中是什么样的呢,我们看如下例子

fun main(args: Array<String>) {
    var array = listOf<String>("hello", "world", "hello world", "welcome", "bye")

    for (item in array) {
        println(item)
    }

    println("------")

    when {
        "world" in array -> println("world in collection")
    }

    //找出长度超过5 字符串大写,默认字符排序,打印控制台 awsome
    array
            .filter { it.length > 5 }
            .map { it.toUpperCase() }
            .sorted().forEach { println(it) }
}
1.找出集合里面的world 使用when表达式 替代了传统的if,这个跟之前的when的使用又不一样
2.实现的流的操作,比之java8的语法更加优雅, it代表lambda表达式的入参

最后一个例子是写class的,跟java中做对比

class EmptyClass

如果往class前面添加public也是可以的,但是编辑器提醒public是多余的,说明默认class就是public的,加不加都可以,其次java中要求public class 类名跟文件名保持一致,在kotlin中没有这样的规定,如果类中什么也没有,可以不带花括号
在看下面

class MyClass constructor(userName : String){
    private val userName: String = userName.toUpperCase()

    init {
        println(userName)
    }
}

fun main(args: Array<String>) {
    val myClass: MyClass = MyClass("zhangsan")
}

1.在kotlin中,一个类可以有个primary构造方法以及一个或者多个secondary构造方法
2.primary构造方法是类头(class head) 的一部分,它位于类名后面,可以拥有若干参数
3.如果primary构造方法没有任何注解或者可见性关节字修饰,那么constructor关键字可以省略
4.初始化MyClass,省略了new关键字
上面的例子可以看到init代码块其实就是类似之前java构造方法的逻辑,这里构造方法,跟初始化构造方法逻辑做了拆分,构造方法要么用于初始化成员变量,要么用于init代码块逻辑,如上面例子,上面例子运行结果
zhangsan

[kotlin] 面向对象特性2

结合上一篇的博客,kotlin class中的构造方法分为primary constructor 只有一个, secondary constructor可以有多个,主构造方法可以给定参数,那么这些参数的初始化会在class 的init代码块中执行,而secondary constructor 的初始化跟参数是在一起的, 且secondary必须直接或者间接调用primary constructor,这部分内容跟java有着很大的不同。constructor 如果没有修饰符,默认是public的修饰符,而java中是default修饰符,这点也有区别
来,上代码

class Person constructor(userName: String) {

    private var userName: String? = null

    private var age:Int

    private var adderss:String

    init {
        println(userName)

        this.userName = userName
        this.age = 20
        this.adderss = "beijing"
    }

    //直接或者间接调用primary方法
    constructor(userName: String, age: Int) : this(userName){
        println("$userName, $age")

        this.age = age
        this.adderss = "beijing"
    }

    //直接或者间接调用primary方法
    constructor(userName: String, age: Int, address: String) : this(userName, age){
        println("$userName, $age, $address")

        this.adderss = adderss
    }

    fun printInfo() {
        println("username: ${this.userName}, age: ${this.age}, ${this.adderss}")
    }
}

fun main(args: Array<String>) {
    var person = Person("zhangsan")
    var person2 = Person("lisi", 12)
    var person3 = Person("lisi", 20, "nanjing")

    person.printInfo()
    person2.printInfo()
    person3.printInfo()
}

由执行结果可以看出调用顺序,先走primary,再走secondary

kotlin还提供了一种简约的类的定义方式
我们可以直接在构造方法入参中,直接定义成员变量,可以给定成员变量的默认值,这种方式一般在真是的成产开发中比较常见,因为谁都不愿意多写代码😠,虽然java有lombok这样的框架减少了很多代码

class Student(private val userName: String, private val age: Int, private var address: String) {

    fun printInfo() {
        println("username: $userName, age: $age, address: $address")
    }
}

/**
 * constructor 有修饰符,或者注解的情况下,constructor关键字不可以省略
 *
 */
class Student2 private constructor(userName: String){

}

/**
 * 构造方法提供默认参数, 如果primary构造方法都有默认值,
 * 在jvm上面如果所有的构造方法都有默认值,编译器会帮我们生成一个不带参数的构造方法,为了去兼容
   第三方比如spring框架
 */
class Student3 (val userName: String = "zhangsan"){}


fun main(args: Array<String>) {
    val student = Student("zhangsan", 20, "beijing")
    student.printInfo()
}

通过代码可以看出来,方法直接可以引用到构造方法定义的值,这个可以通过模板字符串的引用可以看清楚
还有就是一点值得提 在jvm上面如果所有的构造方法都有默认值,编译器会帮我们生成一个不带参数的构造方法,目的是为了去兼容第三方比如spring框架,初始化实例的时候会调用那个不带参数的构造方法,而这个构造方法的初始值会调用所有参数的默认值😙 kotlin规矩确实有点多啊,期待后面新的不同,更有深度的学习输出😊也很期待,在生成环境中使用kotlin浪一把

[kotlin]扩展(extension)

初见扩展

kotlin里面提供了扩展,用起来像js这些动态语言,可以给一个既有的类,提供一个方法,也可以提供一个属性,java中的装饰模式,也是对既有的实现类中,添加额外的功能。

class ExtensionTest {


    fun add(a: Int, b: Int) = a + b

    fun substract(a: Int, b: Int) = a - b
}

//不使用设计模式实现
fun ExtensionTest.multiply(a: Int, b: Int) = a * b

fun main(args: Array<String>) {
    var extensinTest = ExtensionTest()

    println(extensinTest.add(1, 2))
    println(extensinTest.substract(1, 2))
    println(extensinTest.multiply(1, 2))
    myPrint(BB())
    CC().foo()
}

open class AA

class BB: AA()

fun AA.a() = "a"

fun BB.a() = "b"

fun myPrint(aa: AA) {
    println(aa.a())
}

class CC {
    fun foo() {
        println("member")
    }
}

fun CC.foo() {
    println("member2")
}

/**
 * 对any进行扩展
 */
fun Any?.toString(): String {
    if (null == this) {
        return "null"
    }

    return toString()
}

属性的扩展

class MyExtensionProperty {
}
val MyExtensionProperty.name: String
get() = "hello"

fun main(args: Array<String>) {
    var myExtensionProperty = MyExtensionProperty()
    println(myExtensionProperty.name)
}

伴生对象的扩展

class CompanionObjectExtension {
    companion object MyObject{

    }
}

fun CompanionObjectExtension.MyObject.method() {
    println("extension companion")
}

fun main(args: Array<String>) {
    CompanionObjectExtension.method()
}
  • 🚩扩展不会侵入原来的类
  • 🚩扩展是静态分发的,不支持多态,调用只取决于对象的声明
  • 🚩调用是由对象声明决定的,而不是由对象的实际类型决定的

扩展作用域

以上的作用域是全局的,我们来看下koltin的作用域

class ExtensionReciver {

    fun method() {

    }
}

class DispatchReceiver {

    fun method2() {

    }

    fun ExtensionReciver.hello() {
       //can call two method
        method()
        method2()
    }


    /**
     * 在分发接收者里面可以调用扩展方法
     */
    fun world(extensionReciver: ExtensionReciver) {
        extensionReciver.hello()
    }

    /**
     * toString()的优先级最高 3
     */
    fun ExtensionReciver.output() {
        println(toString())
        //如果必须用分发接受这的话,语法是这样
        println(this@DispatchReceiver.toString())
    }

    fun test() {
        var extensionReciver = ExtensionReciver()
        extensionReciver.output()
    }
}

fun main(args: Array<String>) {
    //扩展只能在DispatchReceiver范围内使用
    DispatchReceiver().test()
}
  • 🚩扩展所在的实例叫做分发接受者(dispatch receiver)
  • 🚩扩展函数所扩展的那个类叫做扩展接收者(extension receiver)
  • 🚩当以上两个名字冲突的时候,扩这接收者的优先级最高,换句话说扩展后的方法的优先级最低

summary

为啥要扩展,在jdk中有很多辅助类,Collections 很对静态方法,提供了排序,二分查找的功能,如果koltin中可能直接给list扩展一个业务方法,这样相对简单。但是Collections可以做到复用,不知道koltin这方面怎么实践的。

[kotlin]Kotlin的getter setter,属性延迟初始化

getter setter

kotlin getter,setter 跟java的不一样,我们直接看代码

class ThePerson(address: String, name: String) {

    val age: Int
        get() = 20

    var address = address
    get(){
        println("get invoke")
        //backing field
        return field
    }
    set(value) {
        println("set invoke")
        field = value
    }

    var name = name
//    private set
}

fun main(args: Array<String>) {
    var person = ThePerson("shanghai", "zhangsan")

    println(person.age)

    println(person.address)

    person.address = "beijing"

    println(person.address)

    println(person.name)
    person.name = "hello"
    println(person.name)
}
  • 格式如下var propertyName: propertyType = initializer
    getter()...
    setter()...
  • 我们可以通过person.address 进行对属性的set get,可以看控制台打印结果,就是set get方法的调用
  • 看如上field 为back field支撑字段,读写实际上操作的是支撑字段,field关键字只能在get() set()代码块中使用
  • 字段val只能有get()代码块,var字段默认会生成一个自己的getter setter
    如上理解都比较简单

属性延迟初始化

Kotlin中要求非空类型属性必须在构造方法中初始化:joy:,跟spring一起使用的时候,比如依赖注入,单元测试属性赋值,所以加上lateinit关键字

class TheClass {
    lateinit var name: String

    fun init() {
        this.name = "zhangsan"
    }

    fun print() {
        println(this.name)
    }
}

fun main(args: Array<String>) {
    var theClass = TheClass()
    theClass.init()
    theClass.print()
}
  • lateinit 只能在类体var属性上,不能使用在primary constructor属性上
  • 属性不能有自定义的getter setter
  • 属性类型必须要求非空,而且不能是原生类型

[kotlin]基础特性之when array loop

以前看过的东西不知道输出,最近春节看了kotlin,从最基础的开始,用这样博客的形式开始文档输出,旨在加强学习印象

首先
这个函数转化入参为字符串

 fun main(args: Array<String>) {
    println(convert2Uppercase("hello world"))
    println(convert2Uppercase(23))
}


fun convert2Uppercase(str: Any) : String?{
    if(str is String){
      return str.toUpperCase()
    }

    return null
}

在java中可能需要instanceof 判断然后再进行强制类型装换,kotlin编译器自动帮我们识别类型

在数组循环中,跟es6/typescript支持的有点相似,既能取出元素,也能取出index

fun main(args: Array<String>) {
    var array = intArrayOf(1, 2, 3, 4, 5)

    for (item in array) {
        println(item)
    }

    println("-----")

    for (index in array.indices) {
        println("array[$index] = ${array[index]}")
    }
    println("-----")

    for ((index, value) in array.withIndex()) {
        println("array[$index] = $value")
    }
}

可以打印一个元素,可以打印数组里面的index,可以打印index跟数组内容
看kotlin Int的实现可以看出,在面向jvm的时候,Int是原生数据类型,而intArrayOf返回的是int[]

我们再来看下强大的when

fun main(args: Array<String>) {
    println(myPrint("hello"))
    println(myPrint("world"))
    println(myPrint("hello world"))
    println(myPrint("nothing"))

    println("------")

    var a = 6

    var result = when(a){
        1 -> {
            println("a = 1")
            10
        }
        2 -> {
            println("a = 2")
            20
        }
        3, 4, 5-> {
            println("a =3 or a = 4 or 5")
            30
        }
        in 6..10-> {
            println("in range")
            40
        }
        else -> 50
    }
}

fun myPrint(str: String): String {
    return when (str) {
        "hello" -> "HELLO"
        "world" -> "WORLD"
        "hello world" -> "HELLO WORLD"
        else -> "other input"
    }
}

fun myPrint2(str: String): String = when (str) {
    "hello" -> "HELLO"
    "world" -> "WORLD"
    "hello world" -> "HELLO WORLD"
    else -> "other input"
}

when的使用相对于java中的switch case, ->这样的写法并不是lambda表达式,但是看看myPrint函数,语法比起java要风*👍,也可以按照myPrint2函数的写法,直接返回一个表达式,语法更加简约👍,在最后的例子中,when的条件可以是枚举数,可以是枚举数范围,也可以是一个区间。由上面的例子可以看出kotlin when的语法糖对开发很友好,十分适合写我们平时复杂的业务开发🚀

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.