前言
在java中我们需要扩展一个类的新功能时,一般是继承该类或者使用像装饰者这样的设计模式来实现的。
如下:
public class Animal {
protected String name;
Animal(String name){
this.name = name;
}
public void showName(){
System.out.print(name);
}
}
复制代码
我们想要给这个 类加一个吃东西的功能,这时使用Java继承来实现
实现代码如下:
public class Cat extends Animal {
Cat(String name) {
super(name);
}
//新增加吃东西的功能
public void eat(String food){
System.out.print(name+":吃了 "+food);
}
}
复制代码
这样我们就实现了 吃东西的功能。
而在kotlin我们不用这样实现了,可以通过叫做 ++扩展++ 的特殊声明来完成。Kotlin 支持 ++扩展函数++ 和 ++扩展属性++。
1.扩展函数
声明一个扩展函数我们需要用一个 被扩展的类来作为它的前缀。
公式如下:
fun 被扩展类名.扩展函数名( 参数 ){
//实现代码
}
我们来实现上面java实现的功能,代码如下:
fun Animal.eat(food:String){
print("$name 吃了 $food")
}
复制代码
上面kotlin代码就实现了我们用java继承实现的新功能,那要怎么调用呢 我们可以在kotlin中像调用普通的函数来调用这个扩展函数,代码如下:
val animal = Animal("cat")
animal.eat("apple")
复制代码
上面介绍了kotlin中怎么调用扩展函数,那在Java中怎么调用了,代码如下:
Animal animal = new Animal("cat");
//java 中调用kotlin 扩展函数 Aaa 为扩展函数文件名
AaaKt.eat(animal,"apple");
复制代码
这样我们就在不用继承该类的情况下增加了 吃东西的新功能。
注意: 我们可以从上面的java调用中可以看出,扩展并不是真正的修改了被扩展类。而只是在kotlin中的调用像是修改了被扩展类。
1.2.扩展是静态解析的
在kotlin中扩展是静态分发的,不是根据接收者类型的虚方法。也就是说调用扩展函数是由调用所在的表达式类型来决定的,而不是由表达式运行时求值结果决定的。例如:
//扩展了 Animal 和Cat Cat 继承 Animal
fun Animal.getFood() = "苹果"
fun Cat.getFood() = "猫粮"
//在这个方法中 传入类型是 Animal 所以获取到的是"苹果",不管是传入的是Animal 还是 Animal 的子类 如Cat
fun printFood(food:Animal){
print(food.getFood())
}
fun main(args: Array<String>) {
val cat = Cat("cat")
//如我在这传入的是 Cat 但是 输出出来的还是 苹果
printFood(cat)
}
复制代码
在这个例子会输出“苹果”,因为我们在调用扩展函数时只取决于参数 food 的声明类型,该类型是 Animal 类。
如果扩展函数和被扩展类中的成员函数有相同的接收类型、名字和参数,那么这种情况下 ** 总是取成员**。例如:
class Dog {
fun showName(){
print("Dog")
}
}
fun Dog.showName(){
print("Cat")
}
fun main(args: Array<String>) {
Dog().showName()
}
复制代码
如果我们调用 Dog 类的 showName(),它将输出“Dog”,而不是“Cat”.
这样是不是很不方便,所以扩展函数可以重载相同名字但是不同签名成员函数。例如:
class Dog {
fun showName(){
print("Dog")
}
}
fun Dog.showName(name:String){
print("Cat")
}
fun main(args: Array<String>) {
Dog().showName("")
}
复制代码
这样我们调用 showName("")函数,就会输出 “Cat”了。
1.3 可空接受者
扩展函数可以为可空的接收者类型定义扩展。这样的扩展可以在对象变量上调用,即使其值为 null,并且可以在函数体内检测 this == null ,这样就可以在没有检测 null 的时候调用成员函数了,列如:
fun Dog?.toString():String{
if (this == null) return "null \n"
return toString()
}
fun main(args: Array<String>) {
var dog:Dog? = null
print(dog.toString())
dog = Dog()
print(dog.toString())
}
复制代码
这段代码 第一次打印输出的是 "null \n" 第二次输入的是改对象的地址。
2.扩展属性
与函数类似,Kotlin也支持扩展属性。 例如:
class Snake{
var aaa = 1
}
var Snake.size:Int
set(value) {aaa = value}
get() = aaa +1
fun main(args: Array<String>) {
val snake = Snake()
print(snake.size)
snake.size = 3
print(snake.size)
}
复制代码
如上例子所示。
**注意:**由于扩展是没有实际将变量成员插入类中,因此对扩展属性来说幕后字段是无效的。所以扩展属性不能有初始化器,只能显示的提供 getters/setters 定义。
例如:
//错误:扩展属性不能有初始化
val Snake.bbb = 1
复制代码
如上例子是错误的。
3.伴生对象的扩展
我们知道在kotlin中是没有 static 这个关键字的,那我们要怎么实现静态变量呢,那就用到了伴生对象,例如:
class Snake{
var aaa = 1
companion object {
var Bbb = 1
}
}
fun main(args: Array<String>) {
//这样就达到了Java中静态变量一样的效果了
Snake.Bbb
}
复制代码
那我们怎么对伴生对象进行扩展呢,其实也很简单只是比其他扩展中间加了一个 Companion 。如例:
fun Snake.Companion.foo(){...}
复制代码
如上例,我就就定义了一个名为 foo() 的扩展函数,那我们这调用这个扩展函数了,其实和普通的扩展函数调用方法是一样的,如下例:
Snake.foo()
复制代码
示例地址:github.com/tao11122233…