從Java到Kotlin(四)

對象與泛型html

目錄

  • 1.對象
    1.1 匿名類與對象
    1.2 靜態類成員與伴生對象
  • 2.泛型
    2.1 型變
    2.2 類型投影
    2.3 泛型函數
    2.4 泛型約束

1.對象

1.1 匿名類與對象表達式

Java中有匿名類這個概念,指的是在建立類時無需指定類的名字。在Kotlin中也有功能類似的「匿名類」,叫作對象,舉個例子:安全

Java匿名類bash

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還能夠實現接口,以下所示:ide

//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()
}
複製代碼

運行代碼,查看結果:post

請注意,匿名對象能夠用做只在本地和私有做用域中聲明的類型。若是你使用匿名對象做爲公有函數的返回類型或者用做公有屬性的類型,那麼該函數或屬性的實際類型會是匿名對象聲明的超類型,若是你沒有聲明任何超類型,就會是 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,以下所示:ui

var age = 22
printlnUserName(object : Login() {
    override fun printlnUserName() {
        age = 23
        Toast.makeText(this@MainActivity, "$age", Toast.LENGTH_SHORT).show()
    }
})
複製代碼

運行代碼,查看結果:this

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的泛型沒有提供通配符,取而代之的是outin修飾符。先舉個例子:

//用out修飾T
class Box<out T> {
}
複製代碼

(紅色波浪線標記處爲編譯錯誤)

//用in修飾T
class Box<in T> {
}
複製代碼

(紅色波浪線標記處爲編譯錯誤)

對比上面兩段代碼能夠看出,用out來修飾T,只能消費T類型,不能返回T類型; 用in來修飾T,只能返回T類型,不能消費T類型。簡單來講就是 in 是消費者, out 是生產者。

####2.2 類型投影 上面說到了outin修飾符,若是咱們不用他們來修飾泛型,會出現這種狀況:

class Box<T> {
}
複製代碼

編譯不經過,由於Array對於類型T是不可變的,因此Box和Box誰也不是誰的子類型,因此編譯不經過。對於這種狀況,咱們仍是能夠用 outin修飾符來解決,但不是用來修飾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的基礎上做了一些改進,增長了一些語法糖,更靈活也更安全。

參考文獻:
Kotlin語言中文站、《Kotlin程序開發入門精要》

推薦閱讀:
從Java到Kotlin(一)爲何使用Kotlin
從Java到Kotlin(二)基本語法
從Java到Kotlin(三)類和接口
從Java到Kotlin(四)對象與泛型
從Java到Kotlin(五)函數與Lambda表達式
從Java到Kotlin(六)擴展與委託
從Java到Kotlin(七)反射和註解
從Java到Kotlin(八)Kotlin的其餘技術
Kotlin學習資料總彙


更多精彩文章請掃描下方二維碼關注微信公衆號"AndroidCzh":這裏將長期爲您分享原創文章、Android開發經驗等! QQ交流羣: 705929135

相關文章
相關標籤/搜索