美文网首页
第二章 Java与Kotlin的写法比较

第二章 Java与Kotlin的写法比较

作者: 安卓老菜鸟 | 来源:发表于2020-03-23 18:01 被阅读0次

    3 Java与Kotlin的写法比较

    3.1 构造器、变量、常量和静态数据

    3.1.1 构造函数

    java中的构造函数是函数,和类的名称相同,没有返回类型。

    ```

    public class User {

        int id;

        String name;

        public User(int id, String name) {

            this.id = id;

            this.name = name;

        }

    }

    ```

    kotlin中的则是使用constructor这个关键字。

    ```

    class User {

        val id: Int

        val name: String

        constructor(id: Int, name: String) {

            this.id = id

            this.name = name

        }

    }

    ```

    可以发现有两点不同: 

    Java 中构造器和类同名,Kotlin 中使用 constructor 表示。 

    Kotlin 中构造器没有 public 修饰,因为默认可见性就是公开的

    3.1.2 初始化代码块

    在遇到一些需要在构造方法执行之前执行的情形时,我们通常使用初始化代码块。

    在java中,只需要将代码块用{}包起来,置于构造函数之前就可以,而kotlin的写法大致类似,不同的是需要在{}之前加上init关键字。

    ```

    public class User {

        {

            // 初始化代码块,先于下面的构造器执行

        }

        public User() {

        }

    }

    ```

    ```

    class User {

        init {

            // 初始化代码块,先于下面的构造器执行

        }

        constrouctor() {

        }

    }

    ```

    3.1.3 final

    Kotlin 中的 val和 Java 中的 final类似,表示只读变量,不能修改。这里分别从成员变量、参数和局部变量来和 Java 做对比:

    ```

    final int final1 = 1;

    void method(final String final2) {

        final String final3 = "The parameter is " + final2;

    }

    ```

    ```

    val fina1 = 1

    //参数是没有 val 的,在kotlin中函数参数是默认val的,不允许被修改,减少了出错率

    fun method(final2: String) {

        val final3 = "The parameter is " + final2

    }

    ```

    可以看到不同点主要有: 

    1.final 变成了 val。 

    2.Kotlin 函数参数默认是 val 类型,所以参数前不需要写 val 关键字,Kotlin 里这样设计的原因是保证了参数不会被修改,而 Java 的参数可修改(默认没 final 修饰)会增加出错的概率。

    不过 val 和 final 还是有一点区别的,虽然 val 修饰的变量不能二次赋值,但可以通过自定义变量的 getter 函数,让变量每次被访问时,返回动态获取的值:

    ```

    val size: Int

        get() { //每次获取 size 值时都会执行 items.size

            return items.size

        }

    ```

    不过这个属于 val 的另外一种用法,大部分情况下 val 还是对应于 Java 中的 final 使用的。

    final在java中最被常用的地方,便是用来修饰常量,并且通常和static连用

    ```

    public static final int CODE_ONE = 1;

    ```

    在kotlin中,虽然final可以用val来修饰,但是static却没有直接的相对应的关键字。在kotlin中,是没有静态常量以及静态方法这两个概念的,若是想要像在java中直接类引用,就使用companion object

    ```

    companion object{

        val codeOne : Int = 1; //静态常量

        fun getHH(a : Int,b : Int) : Int {

            return a*b

        }//此为静态方法

    }

    ```

    3.1.4 object

    Kotlin 里的 object ——首字母小写的,不是大写,Java 里的 Object 在 Kotlin 里不用了。   

    Java 中的 Object 在 Kotlin 中变成了 Any,和 Object 作用一样:作为所有类的基类。

    而 object 不是类,像 class 一样在 Kotlin 中属于关键字: 

    它的意思很直接:创建一个类,并且创建一个这个类的对象。这个就是 object 的意思:对象。

    ```

    object Sample {

        val name = "A name"

    }

    ```

    在代码中如果要使用这个对象,直接通过它的类名就可以访问:

    ```

    Sample.name

    ```

    这就是java中的单例,不过kotlin创建单例的方法比java要简单许多。

    Java 中实现单例类(非线程安全): 

    ```

    public class A {

        private static A sInstance;

        public static A getInstance() {

            if (sInstance == null) {

                sInstance = new A();

            }

            return sInstance;

        }

        //还有很多模板代码

        ...

    }

    ```

    ```

    // class 替换成了 object

    object A {

        val number: Int = 1

        fun method() {

            println("A.method()")

        }

    ```

    和 Java 相比的不同点有: 

    1.和类的定义类似,但是把 class 换成了 object 。

    不需要额外维护一个实例变量 sInstance。

    不需要「保证实例只创建一次」的 getInstance() 方法。 

    2.这种通过 object 实现的单例是一个饿汉式的单例,并且实现了线程安全。

    继承类和实现接口

    Kotlin 中不仅类可以继承别的类,可以实现接口,object 也可以:

    ```

    open class A {

        open fun method() {

            ...

        }

    }

    interface B {

        fun interfaceMethod()

    }

    object C : A(), B {

        override fun method() {

            ...

        }

        override fun interfaceMethod() {

            ...

        }

    }

    ```

    为什么 object 可以实现接口呢? 

    简单来讲 object 其实是把两步合并成了一步,既有 class 关键字的功能,又实现了单例,这样就容易理解了。 

    3.2 数组和集合

    3.2.1 数组

    声明一个 String 数组:

    ```

    String[] strs = {"a", "b", "c"};

    ```

    ```

    val strs: Array<String> = arrayOf("a", "b", "c")

    ```

    可以看到 Kotlin 中的数组是一个拥有泛型的类,创建函数也是泛型函数,和集合数据类型一样。 

    将数组泛型化有什么好处呢?对数组的操作可以像集合一样功能更强大,由于泛型化,Kotlin 可以给数组增加很多有用的工具函数: 

    get() / set() 

    contains() 

    first() 

    find()

    这样数组的实用性就大大增加了。

    取值和修改

    Kotlin 中获取或者设置数组元素和 Java 一样,可以使用方括号加下标的方式索引:

    ```

    println(strs[0])

    strs[1] = "B"

    ```

    不支持协变

    Kotlin 的数组编译成字节码时使用的仍然是 Java 的数组,但在语言层面是泛型实现,这样会失去协变 (covariance) 特性,就是子类数组对象不能赋值给父类的数组变量:

    ```

    val strs: Array<String> = arrayOf("a", "b", "c")

    val anys: Array<Any> = strs // compile-error: Type mismatch

    ```

    而这在 Java 中是可以的:

    ```

    String[] strs = {"a", "b", "c"};

    Object[] objs = strs; // success

    ```

    3.2.2 集合

    Kotlin 和 Java 一样有三种集合类型:List、Set 和 Map,它们的含义分别如下: 

    1.List 以固定顺序存储一组元素,元素可以重复。   

    2.Set 存储一组互不相等的元素,通常没有固定顺序。 

    3.Map 存储 键-值 对的数据集合,键互不相等,但不同的键可以对应相同的值。

    从 Java 到 Kotlin,这三种集合类型的使用有哪些变化呢?我们依次看看。 

    List

    Java 中创建一个列表:

    ```

    List<String> strList = new ArrayList<>();

    strList.add("a");

    strList.add("b");

    strList.add("c"); //添加元素繁琐

    ```

    Kotlin 中创建一个列表:

    ```

    val strList = listOf("a", "b", "c")

    ```

    首先能看到的是 Kotlin 中创建一个 List 特别的简单,有点像创建数组的代码。而且 Kotlin 中的 List 多了一个特性:支持 covariant(协变)。也就是说,可以把子类的 List 赋值给父类的 List 变量: 

    Kotlin:

    ```

    val strs: List<String> = listOf("a", "b", "c")

    val anys: List<Any> = strs // success

    ```

    而这在 Java 中是会报错的:

    ```

    List<String> strList = new ArrayList<>();

    List<Object> objList = strList; //compile error: incompatible types

    ```

    Sequence

    这是在kotlin中新出现的集合,类似于java中的Iterable,可以用来遍历一组数据并可以对每个元素进行特定的处理。

    类似 listOf() ,使用一组元素创建:

    ```

    sequenceOf("a", "b", "c")

    ```

    使用 Iterable 创建:

    ```

    val list = listOf("a", "b", "c")

    list.asSequence()

    ```

    这里的 List 实现了 Iterable 接口。

    使用 lambda 表达式创建:

    ```

    //第一个元素

    val sequence = generateSequence(0) { it + 1 }

    //lambda 表达式,负责生成第二个及以后的元素,it 表示前一个元素

    ```

    3.3 可见性修饰符

    讲完了数据集合,再看看 Kotlin 中的可见性修饰符,Kotlin 中有四种可见性修饰符: 

    public:公开,可见性最大,哪里都可以引用。 

    private:私有,可见性最小,根据声明位置不同可分为类中可见和文件中可见。 

    protected:保护,相当于 private + 子类可见。 

    internal:内部,仅对 module 内可见。

    相比 Java 少了一个 default 「包内可见」修饰符,多了一个 internal「module 内可见」修饰符。这一节结合例子讲讲 Kotlin 这四种可见性修饰符,以及在 Kotlin 和 Java 中的不同。 

    3.3.1 public

    在Java中不写可见性修饰符的类,仅包内可见,即在package范围内调用。若是要在包之外的地方调用,便需要加上public修饰符。 

    在kotlin中也有public,默认不写的可见性修饰就表示public,可见范围与java中的public一样。

    Ps :注意,kotlin中的类不光是默认public的,更是默认final的,所以想继承该类,必须使用open关键字。

    3.3.2 @hide

    在 Android 的官方 sdk 中,有一些方法只想对 sdk 内可见,不想开放给用户使用(因为这些方法不太稳定,在后续版本中很有可能会修改或删掉)。 

    为了实现这个特性,会在方法的注释中添加一个 Javadoc 方法 @hide,用来限制客户端访问:

    ```

    /**

    * @hide

    */

    public void hideMethod() {

        ...

    }

    ```

    这种方法还是可以通过反射拿到的,针对此类情形,kotlin中有一个更加严格的可见性修饰符————internal。 

    3.3.3 internal

    internal 表示修饰的类、函数仅对 module 内可见,这里的 module 具体指的是一组共同编译的 kotlin 文件,常见的形式有:

    Android Studio 里的 module

    Maven project

    我们常见的是 Android Studio 中的 module 这种情况,Maven project 仅作了解就好,不用细究。

    internal 在写一个 library module 时非常有用,当需要创建一个函数仅开放给 module 内部使用,不想对 library 的使用者可见,这时就应该用  internal 可见性修饰符。 

    3.3.4 protected

    Java 中 protected 表示包内可见(default) + 子类可见。 

    Kotlin 中 protected 表示 private + 子类可见。 

    Kotlin 相比 Java protected 的可见范围收窄了,原因是 Kotlin 中不再有「包内可见」的概念了,相比 Java 的可见性着眼于 package,Kotlin 更关心的是 module。 

    3.3.5 private

    Java 中的 private 表示类中可见,作为内部类时对外部类「可见」。 

    Kotlin 中的 private  表示类中或所在文件内可见,作为内部类时对外部类「不可见」。

    ```

    class Sample {

        private val propertyInClass = 1 //仅 Sample 类中可见

    }

    private val propertyInFile = "A string." //范围更大,整个文件可见

    ```

    java和kotlin中用private来修饰内部类中变量的的区别:

    ```

    //在java中可以直接获取内部类的private类变量

    public class Outter {

        public void method() {

            Inner inner = new Inner();

            int result = inner.number * 2; // success

        }

        private class Inner {

            private int number = 0;

        }

    }

    ```

    ```

    //在kotlin中不能直接获取内部类的private类变量

    class Outter {

        function method() {

            val inner : Inner  = Inner();

            val result : Int = inner.number * 2;

            //compile-error: Cannot access 'number': it is private in 'Inner'

        }

        private class Inner {

            private val number : Int  = 0;

        }

    }

    ```

    可以修饰类和接口: 

    Java 中一个文件只允许一个外部类,所以 class 和 interface 不允许设置为 private,因为声明 private 后无法被外部使用,这样就没有意义了。

    Kotlin 允许同一个文件声明多个 class 和 top-level 的函数和属性,所以 Kotlin 中允许类和接口声明为 private,因为同个文件中的其它成员可以访问:

    ```

    private class Sample {

        val number = 1

        fun method() {

            println("Sample method()")

        }

    }

    //在同一个文件中,所以可以访问

    val sample = Sample()

    ```

    相关文章

      网友评论

          本文标题:第二章 Java与Kotlin的写法比较

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