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()
```
网友评论