如果您是一位经验丰富的Android应用程序开发人员,您可能已经习惯Java 7的冗长了。因此,您可能会发现Kotlin的简洁语法(该语言适用于函数式程序员)略有不安。
初学者在学习Kotlin时遇到的一个常见问题是了解如何期望您使用包含单个方法的Java接口。 这样的接口在Android世界中无处不在,通常被称为SAM接口,其中SAM是Single Abstract Method的缩写。
在这个简短的教程中,您将学到在Kotlin代码中恰当地使用Java的SAM接口所需的一切。
1.什么是SAM转换?
当您想使用Kotlin代码中包含单个方法的Java接口时,不必手动创建实现该方法的匿名类。 相反,您可以使用lambda表达式。 由于有一个称为SAM转换的过程,Kotlin可以将其签名与接口的单个方法的签名匹配的任何lambda表达式透明地转换为实现该接口的匿名类的实例。
例如,考虑以下一种方法的Java接口:
public interface Adder {
public void add(int a, int b);
}
使用上述接口的一种幼稚且类似于Java 7的方法将涉及使用object
表达式,并且看起来像这样:
// Creating instance of an anonymous class
// using the object keyword
val adder = object : Adder {
override fun add(a: Int, b: Int): Int {
return a + b
}
}
那是很多不必要的代码,也不太可读。 但是,通过利用Kotlin的SAM转换工具,您可以编写以下等效代码:
// Creating instance using a lambda
val adder = Adder { a, b -> a + b }
如您所见,我们现在用一个简短的lambda表达式替换了匿名类,该表达式以接口名称为前缀。 请注意,lambda表达式采用的参数数量等于接口方法签名中的参数数量。
2.函数调用中的SAM转换
使用具有以SAM类型作为参数的方法的Java类时,可以进一步简化上述语法。 例如,考虑下面的Java类,该类包含一个期望实现Adder
接口的对象的方法:
public class Calculator {
private Adder adder;
public void setAdder(Adder adder) {
this.adder = adder;
}
public void add(int a, int b) {
Log.d("CALCULATOR", "Sum is " + adder.add(a,b));
}
}
现在,在您的Kotlin代码中,您可以直接将lambda表达式传递给setAdder()
方法,而无需在其前面加上Adder
接口的名称。
val calculator = Calculator()
calculator.setAdder({ a, b -> a+b })
值得注意的是,在调用以SAM类型作为唯一参数的方法时,您可以随意跳过括号以使代码更加简洁。
calculator.setAdder { a, b -> a+b }
3.不带Lambda的SAM转换
如果您认为lambda表达式令人困惑,那么我对您来说是个好消息:SAM转换也可以与普通函数一起正常工作。 例如,考虑以下函数,其签名与Adder
接口的方法的签名匹配:
fun myCustomAdd(a:Int , b:Int):Int =
if (a+b < 100)
-1
else if (a+b < 200)
0
else
a+b
Kotlin允许您直接将myCustomAdd()
函数作为参数传递给Calculator
类的setAdder()
方法。 不要忘记使用::
运算符来引用该方法。 这是如何做:
calculator.setAdder (this::myCustomAdd)
4. it
变量
SAM接口很多时候都包含一参数方法。 顾名思义,单参数方法的签名中只有一个参数。 在使用此类接口时,Kotlin允许您省略lambda表达式签名中的参数,并在表达式的主体中使用称为it
的隐式变量。 为了使事情更清楚,请考虑以下Java接口:
public interface Doubler {
public int doubleIt(int number);
}
在Kotlin代码中使用Doubler
接口时,无需在lambda表达式的签名中明确提及number
参数。 相反,您可以简单地将其称为it
。
// This lambda expression using the it variable
val doubler1 = Doubler { 2*it }
// is equivalent to this ordinary lambda expression
val doubler2 = Doubler { number -> 2*number }
5. Kotlin中的SAM接口
作为Java开发人员,您可能倾向于在Kotlin中创建SAM接口。 但是,这样做通常不是一个好主意。 如果您在Kotlin中创建SAM接口,或创建一个Kotlin方法(期望将实现SAM接口的对象作为参数),则SAM转换工具将不可用-SAM转换是Java的互操作性功能,并且仅限于Java类和接口。
由于Kotlin支持高阶函数(可以将其他函数作为参数的函数),因此您无需在其中创建SAM接口。 例如,如果用Kotlin重写Calculator
类,则可以将其setAdder()
方法编写为直接将函数作为参数,而不是实现Adder
接口的对象。
class Calculator {
var adder:(a:Int, b:Int)->Int = {a,b -> 0}
// Default implementation
// Setter is available by default
fun add(a:Int, b:Int) {
Log.d("CALCULATOR", "Sum is " + adder(a,b))
}
}
在使用上述类时,可以使用=
运算符将adder
设置为函数或lambda表达式。 以下代码向您展示了如何:
val calculator = Calculator()
calculator.adder = this::myCustomAdd
// OR
calculator.adder = {a,b -> a+b}
结论
Android的API主要是用Java编写的,许多API广泛使用SAM接口。 大多数第三方库也可以这样说。 通过使用在本教程中学到的技术,您可以以简洁易懂的方式在Kotlin代码中使用它们。