Kotlin学习指南—泛型、异常

1. 泛型

概念:
在不指定代码中使用到的确切类型的情况下来编写算法。可以创建函数或者类型。

优点:
提高代码可重用性。

1.1 泛型类

定义一个泛型如下

class TypedClass<T>(parameter: T) {
    val value: T = parameter
}

使用上面的泛型

val t1 = TypedClass<String>("Hello World!")
val t2 = TypedClass<Int>(25)

更加简洁的用法可以省略<>类型,让程序自行推断类型

val t1 = TypedClass("Hello World!")
val t2 = TypedClass(25)
val t3 = TypedClass<String?>(null)

如第三个对象接收一个null引用,那仍然还是需要指定它的类型,因为它不能去推断出来。

限制泛型参数非空,可以这样定义

class TypedClass<T : Any>(parameter: T) { 
    val value: T = parameter
}

如果想要 限定泛型参数类型为Context的子类,可以做如下定义

class TypedClass<T : Context>(parameter: T) { 
    val value: T = parameter
}

1.2 泛型函数

定义泛型参数函数

fun <T> typedFunction(item: T): List<T> {
    ...
}

1.3 变体

对泛型参数的通配符的限定、泛型擦除等概念

举例1 说明

List<String> strList = ...;
List<Object> objList = ...;
objList.addAll(strList);

这样是可以的,因为定义在Collection接口中的addAll()是这样的:

List<String>
interface Collection<E> ... {
    void addAll(Collection<? extends E> items);
}

增加Strings到ObjectList

void copyStrings(Collection<? super String> to, Collection<String> from) {
    to.addAll(from);
}

增加Strings到另一个集合中唯一的限制就是那个集合接收Strings或者父类。

但是通配符都有它的限制。通配符定义了使用场景变体(use-site variance),这意味着当我们使用它的时候需要声明它。这表示每次我们声明一个泛型变量时都会增加模版代码。

class TypedClass<T> {
    public T doSomething(){
        ...
    }
}

不合法使用,无法通过编译

TypedClass<String> t1 = new TypedClass<>();
TypedClass<Object> t2 = t1;

合法使用

TypedClass<String> t1 = new TypedClass<>();
TypedClass<? extends String> t2 = t1;

这会让代码更加难以理解,而且增加了一些额外的模版代码。

针对通配符的弊端,Kotlin通过协变(covariance)和逆变(contravariance)来解决。

相比冗长的通配符,Kotlin仅仅使用out来针对协变(covariance)和使用in来针对逆变(contravariance)。

当我们类产生的对象可以被保存到弱限制的变量中,我们使用协变。

class TypedClass<out T>() {
    fun doSomething(): T {
        ...
    }
}

使用上面定义的泛型类创建对象

val t1 = TypedClass<String>()
val t2: TypedClass<Any> = t1

1.4 变体实例

泛型使用举例

1.4.1 let

它接收一个函数(接收一个对象,返回函数结果)作为参数,作为参数的函数返回的结果作为整个函数的返回值。它在处理可null对象的时候是非常有用的,下面是它的定义:

inline fun <T, R> T.let(f: (T) -> R): R = f(this)

通常的非空判定

if (forecast != null) dataMapper.convertDayToDomain(forecast) else null

使用let简化使用

forecast?.let { dataMapper.convertDayToDomain(it) }

多亏?.操作符,let函数只会在forecast不是null的时候才会执行。否则它会返回null。也就是我们想达到的效果。

1.4.2 with

with接收一个对象和一个函数,这个函数会作为这个对象的扩展函数执行。这表示我们根据推断可以在函数内使用this。

定义

inline fun <T, R> with(receiver: T, f: T.() -> R): R = receiver.f()

T代表接收类型,R代表结果。如你所见,函数通过f: T.() -> R声明被定义成了扩展函数。这就是为什么我们可以调用receiver.f()。

fun convertFromDomain(forecast: ForecastList) = with(forecast) {
    val daily = dailyForecast map { convertDayFromDomain(id, it) }
    CityForecast(id, city, country, daily)
}

1.4.3 apply

和with非常像,apply可以避免builder的方式来使用,因为对象调用的函数可以根据自己的需要来初始化自己,然后apply函数会返回它同一个对象

定义

inline fun <T> T.apply(f: T.() -> Unit): T { f(); return this }

使用

val textView = TextView(context).apply {
    text = "Hello"
    hint = "Hint"
    textColor = android.R.color.white
}

它创建了一个TextView,修改了一些属性,然后赋值给一个变量。

让我们用在当前的代码中。在ToolbarManager中,我们使用这种方式来创建导航drawable:

private fun createUpDrawable() = with(DrawerArrowDrawable(toolbar.ctx)) {
    progress = 1f
    this
}

使用with和返回this是非常清晰的,但是使用apply可以更加简单:

private fun createUpDrawable() = DrawerArrowDrawable(toolbar.ctx).apply {
    progress = 1f
}

2. 异常(Exceptions)

在Kotlin中,所有的Exception都是实现了Throwable,含有一个message且未经检查。这表示我们不会强迫我们在任何地方使用try/catch。

Kotlin中抛出异常和捕获异常的方式和Java非常相似。

抛出异常

throw MyException("Exception message")

捕获异常

try{
    // 一些代码
}
catch (e: SomeException) {
    // 处理
}
finally {
    // 可选的finally块
}

在Kotlin中,throw和try都是表达式,这意味着它们可以被赋值给一个变量。这个在处理一些边界问题的时候确实非常有用:

val s = when(x){
    is Int -> "Int instance"
    is String -> "String instance"
    else -> throw UnsupportedOperationException("Not valid type")
}
或者

```kotlin
val s = try { x as String } catch(e: ClassCastException) { null }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Calvin880828

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值