学习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(四)对象与泛型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学习Kotlin(三)类和接口
- 下一篇: 学习Kotlin(五)函数与Lambda