欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

学习Kotlin(四)对象与泛型

发布时间:2025/3/21 编程问答 27 豆豆
生活随笔 收集整理的这篇文章主要介绍了 学习Kotlin(四)对象与泛型 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

 

推荐阅读:
学习Kotlin(一)为什么使用Kotlin
学习Kotlin(二)基本语法
学习Kotlin(三)类和接口
学习Kotlin(四)对象与泛型
学习Kotlin(五)函数与Lambda表达式
学习Kotlin(六)扩展与委托
学习Kotlin(七)反射和注解
学习Kotlin(八)其他技术
Kotlin学习资料总汇

 

目录

  • 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 {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);printlnUserName(new Login("Czh") {@Overridepublic 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是一个对象,该对象继承自上面的LoginprintlnUserName(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 = 1var b = 2}Toast.makeText(this, "${abc.a}${abc.b}", Toast.LENGTH_SHORT).show() }

运行代码,查看结果:

请注意,匿名对象可以用作只在本地和私有作用域中声明的类型。如果你使用匿名对象作为公有函数的返回类型或者用作公有属性的类型,那么该函数或属性的实际类型会是匿名对象声明的超类型,如果你没有声明任何超类型,就会是 Any。在匿名对象中添加的成员将无法访问。如下所示:

 

class User {// 私有函数,所以其返回类型是匿名对象类型private fun getUserName() = object {val userName = "Czh"}// 公有函数,所以其返回类型是 Anyfun getAge() = object {val age = 22}fun get() {getUserName().userName//getAge().age //编译错误} }
  • 内部类访问作用域内的变量

就像 Java 匿名内部类一样,Java可以用final声明变量,使匿名内部类可以使用来自包含它的作用域的变量。如下所示:

final int age = 22; printlnUserName(new Login() {@Overridepublic void printlnUserName() {//因为age用final声明,所以不能修改if (age == 22){return;}} });

而Kotlin在匿名对象中可以任意访问或修改变量age,如下所示:

var age = 22 printlnUserName(object : Login() {override fun printlnUserName() {age = 23Toast.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<in String> = 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的基础上作了一些改进,增加了一些语法糖,更灵活也更安全。


原文链接:https://juejin.im/post/5a805c7c6fb9a0634f40956e

总结

以上是生活随笔为你收集整理的学习Kotlin(四)对象与泛型的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。