对象与泛型
目录
1. 对象
1.1 匿名类与对象
1.2 静态类成员与伴生对象
2. 泛型
2.1 型变
2.2 类型投影
2.3 泛型函数
2.4 泛型约束
1. 对象
1.1 匿名类与对象表达式
Java 中有匿名类这个概念, 指的是在创建类时无需指定类的名字在 Kotlin 中也有功能相似的匿名类, 叫做对象, 举个例子:
Java 匿名类
- public class Login {
- private String userName;
- public Login(String userName) {
- this.userName = userName;
- }
- public void printlnUserName() {
- System.out.println(userName);
- }
- }
- public class JavaActivity extends AppCompatActivity {
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- printlnUserName(new Login("Czh") {
- @Override
- public void printlnUserName() {
- super.printlnUserName();
- }
- });
- }
- public void printlnUserName(Login login) {
- login.printlnUserName();
- }
- }
Kotlin 实现上面的代码, 要用关键字 object 创建一个继承自某个 (或某些) 类型的匿名类的对象, 如下所示:
- class KotlinActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- //object 是一个对象, 该对象继承自上面的 Login
- printlnUserName(object : Login("Czh") {
- override fun printlnUserName() {
- }
- })
- }
- fun printlnUserName(login: Login) {
- login.printlnUserName()
- }
- }
对象 object 还可以实现接口, 如下所示:
- //View.OnClickListener 是一个 interface
- button.setOnClickListener(object : View.OnClickListener {
- override fun onClick(v: View?) {
- }
- })
对象和类一样, 只能有一个父类, 但可以实现多个接口, 多个超类型跟在冒号: 后面用逗号, 分隔 如果只想建立一个对象, 不继承任何类, 不实现任何接口, 可以这样写:
- fun foo() {
- val abc = object {
- var a = 1
- var b = 2
- }
- Toast.makeText(this, "${abc.a}${abc.b}", Toast.LENGTH_SHORT).show()
- }
运行代码, 查看结果:
请注意, 匿名对象可以用作只在本地和私有作用域中声明的类型如果你使用匿名对象作为公有函数的返回类型或者用作公有属性的类型, 那么该函数或属性的实际类型会是匿名对象声明的超类型, 如果你没有声明任何超类型, 就会是 Any 在匿名对象中添加的成员将无法访问如下所示:
- class User {
- // 私有函数, 所以其返回类型是匿名对象类型
- private fun getUserName() = object {
- val userName = "Czh"
- }
- // 公有函数, 所以其返回类型是 Any
- fun getAge() = object {
- val age = 22
- }
- fun get() {
- getUserName().userName
- //getAge().age // 编译错误
- }
- }
内部类访问作用域内的变量
就像 Java 匿名内部类一样, Java 可以用 final 声明变量, 使匿名内部类可以使用来自包含它的作用域的变量如下所示:
- final int age = 22;
- printlnUserName(new Login() {
- @Override
- public void printlnUserName() {
- // 因为 age 用 final 声明, 所以不能修改
- if (age == 22){
- return;
- }
- }
- });
而 Kotlin 在匿名对象中可以任意访问或修改变量 age, 如下所示:
- var age = 22
- printlnUserName(object : Login() {
- override fun printlnUserName() {
- age = 23
- Toast.makeText(this@MainActivity, "$age", Toast.LENGTH_SHORT).show()
- }
- })
运行代码, 查看结果:
1.2 伴生对象
Java 中有静态类成员, 而 Kotlin 中没有, 要实现像静态类成员的功能, 就要用到伴生对象
Java 静态成员:
- class User {
- static User instance = new User();
- public void printlnUser() {
- }
- }
- // 调用
- User.instance.printlnUser()
Kotlin 类内部的对象声明可以用 companion 关键字标记:
- class User {
- companion object {
- var instance = User()
- }
- fun printlnUser() {
- }
- }
- // 调用
- User.instance.printlnUser()
泛型
2.1 型变
Java 泛型
- public class Box < T > {
- public T value;
- public Food(T t) {
- value = t;
- }
- }
- new Box < String > ("123");
- new Box < Integer > (1);
对应的 Kotlin 泛型
- class Box<T>(t: T) {
- var value = t
- }
- var box: Box<String> = Box("123")
- var box2: Box<Int> = Box(123)
可以看出 Java 跟 Kotlin 定义泛型的方法都是差不多的, 不同的是 Java 中的泛型有通配符, 而 Kotlin 没有举个例子:
- List<String> strings = new ArrayList<String>();
- List<Object> objects = strings;// 编译错误
Java 编译器不认为 List 是 List 的子类, 所以编译不通过那我们换种写法:
- List<String> strings = new ArrayList<String>();
- List<Object> objects = new ArrayList<Object>();
- objects.addAll(strings);// 编译通过
为什么调用 addAll()方法就能编译通过呢, 看一下他的源码:
boolean addAll(Collection < ?extends E > c);
Java 泛型提供了问号? 通配符, 上面的 <? extends E > 代表此方法接受 E 或者 E 的 一些子类型对象的集合所以可以通过 addAll() 方法把 List 赋值给 List
Kotlin 的泛型没有提供通配符, 取而代之的是 out 和 in 修饰符先举个例子:
- // 用 out 修饰 T
- class Box<out T> {
- }
(红色波浪线标记处为编译错误)
- // 用 in 修饰 T
- class Box<in T> {
- }
(红色波浪线标记处为编译错误)
对比上面两段代码可以看出, 用 out 来修饰 T, 只能消费 T 类型, 不能返回 T 类型; 用 in 来修饰 T, 只能返回 T 类型, 不能消费 T 类型简单来说就是 in 是消费者, out 是生产者
- ####2.2 类型投影 上面说到了 out 和 in 修饰符, 如果我们不用他们来修饰泛型, 会出现这种情况:
- class Box<T> { }
编译不通过, 因为 Array 对于类型 T 是不可变的, 所以 Box 和 Box 谁也不是谁的子类型, 所以编译不通过对于这种情况, 我们还是可以用 out 和 in 修饰符来解决, 但不是用来修饰 Box, 如下所示:
- fun test(strs: Box < Any > ) {
- var objects: Box < inString > =strs
- // 编译通过
- }
- fun test2(strs: Box < String > ) {
- var objects: Box < out Any > =strs
- // 编译通过
- }
上面的解决方式叫做类型投影, Box 相当于 Java 的 Box<? extends Object>Box 相当于 Java 的 Box<? super Object>
2.3 泛型函数
不仅类可以有类型参数函数也可以有类型参数要放在函数名称之前:
- fun <T> singletonList(item: T): List<T> {
- //
- }
- // 调用
- val l = singletonList<Int>(1)
- singletonList(l)
类似于 Java 的泛型方法:
- public < T > T singletonList(T item) {
- //
- }
- // 调用
- singletonList(1);
2.4 泛型约束
泛型约束能够限制泛型参数允许使用的类型, 如下所示:
Kotlin 代码
- fun < T: Comparable < T >> sort(list: List < T > ) {}
- sort(1) // 编译错误
- sort(listOf(1)) // 编译通过
上述代码把泛型参数允许使用的类型限制为 List
Java 中也有类似的泛型约束, 对应的代码如下:
- public static <T extends Comparable> List<T> sort(List<T> list){
- }
如果没有指定泛型约束, Kotlin 的泛型参数默认类型上界是 Any,Java 的泛型参数默认类型上界是 Object
### 总结 本篇文章对比了 Java 匿名类静态类与 Kotlin 对象的写法和两种语言中对泛型的使用相对来说, Kotlin 还是在 Java 的基础上作了一些改进, 增加了一些语法糖, 更灵活也更安全
参考文献:
Kotlin 语言中文站 Kotlin 程序开发入门精要
来源: https://juejin.im/post/5a805c7c6fb9a0634f40956e