1,扩展类
扩展类,就是在现有类的基础上,添加一些属性或者方法,当然扩展的这些成员需要导入当前扩展成员所在的包才可以访问到。可以不用实现装饰着模式。
data class Coordinate(val x: Double, val y: Double)
val Coordinate.theta: Double
get() {
return Math.atan(y/x)
}
fun Coordinate.R():Double{
return Math.hypot(x, y)
}
这样调用:
val coord = Coordinate(3.0,4.0)
println(coord.theta)
println(coord.R())
那么这个扩展有什么限制呢?
-
在扩展成员当中,只能访问被扩展类在当前作用域内可见的成员,本例中的x和y都是public的(Kotlin默认public,这个我们后面会提到),所以可以在扩展方法和属性中直接访问。
-
扩展成员与被扩展类的内部成员名称相同时,扩展成员将无法被访问到
Java中会自定义一些LogUtils类来打日志,或者直接用android.util.log来输出日志。同时在相应的地方都有写TAG或者饮用静态常量。在kotlin中这样可以实现:
package com.netring.edz.kartuapplication.utils import android.util.Log class LogUtils { inline fun <reified T> T.debug(log: Any){ Log.d(T::class.simpleName,log.toString()) } } //有了这个方法,你可以在任何类的方法体中 debugLog("ss") inline fun <reified T> T.debugLog(log: Any){ Log.d(T::class.simpleName,log.toString()) }
Kotlin关键字一览表(https://blog.csdn.net/sinat_23328795/article/details/78956919 )
我们就很容易看到Kotlin的标准库提供的类似with和apply这样的方法是怎么工作的了:
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
我们通常会在某个方法体内创建一个对象并返回它,可我们除了调用它的构造方法之外还需要做一些其他的操作,于是就要创建一个局部变量。。。有了apply这个扩展方法,我们就可以这么写:
fun getStringBuilder: StringBuilder{
return StringBuilder().apply{
append("whatever")
}
}
这样返回的StringBuilder对象实际上是包含"whatever"这个字符串的。
还有就是Fragment的创建实例生成便是用的 apply:
@JvmStatic fun newInstance(param1: String, param2: String) = HomeFragment().apply { arguments = Bundle().apply { putString(ARG_PARAM1, param1) putString(ARG_PARAM2, param2) } }
2,函数式支持(Lambdas)
常见的Android中的点击事件
textView.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View view){
//todo
}
});
kotlin中这样:
textView.setOnClickListener{ /*todo*/ }
3,UI线程和非UI线程的切换问题。也许你会用handler不断的post。
原来在java当中,我们这么写:
handler.post(new Runnable(){
@Override
public void run(){
//todo
}
});
MainActivity.this.runOnUiThread(
public void run(){
//todo
}
});
而在Kotlin当中呢,我们只需要这么写:
async() {
//do something asynchronously
uiThread {
//do something on UI thread
}
}
详细例子:
主线程是负责UI呈现和交互的,我们不应该因其它运行时间长的任务阻塞它,这将会影响UI性能。在HTTP请求情况下, Android SDK甚至通过抛出一个异常来阻止我们这么做。在Android典型的解决方案是使用AsyncTask
。AsyncTask
有一个doInBackground
抽象方法,其在另个线程中执行。
除了让AsyncTask
正常工作很难这一事实外,由于它自身带来了许多问题,使得通过它扩展创建一个新类、在onDestroy
中终止它等等,都是很乏味。这个(你可能需要更多的检查以避免崩溃)非常简单的版本将是:
1 @Override protected void onCreate(Bundle savedInstanceState) { 2 super.onCreate(savedInstanceState); 3 4 task = new AsyncTask<Void, Void, String>() { 5 @Override protected String doInBackground(Void... params) { 6 return requestFromServer("<api call>"); 7 } 8 9 @Override protected void onPostExecute(String s) { 10 if (!isFinishing() && !isCancelled()) { 11 Log.d("Request", s); 12 Toast.makeText(ExampleActivity.this, "Request performed", Toast.LENGTH_LONG).show(); 13 } 14 } 15 }; 16 } 17 18 @Override protected void onDestroy() { 19 super.onDestroy(); 20 21 if (task != null) { 22 task.cancel(true); 23 task = null; 24 } 25 }
这实在不清晰也不直观。当我们在Android中用Kotlin开发时,我们不能忘记Anko库。它主要目的是提供DSL方式用代码来创建布局,而不是用XML。我实际使用过XML,所以我现在不使用它了,但是它还是包括一整套非常有用的特性。特别对异步任务有些小的DSL。这样在Kotlin中,前面的代码能够减少为:
1 async { 2 val result = URL("<api call>").readText() 3 uiThread { 4 Log.d("Request", result) 5 longToast("Request performed") 6 } 7 }
实际上,你有async
函数,它将在另一个线程中执行代码,并由uiThread
给出返回主线的机会。async
是Context
的扩展函数实现,且使用它弱应用,所以不会阻止GC释放内存。
uiThread
优势的方面是它依据使用类,以用不同的方式来实现。如果我们从Activity中调用它,假设actiivity.isFinishing()
返回true
,uiThread
代码是不会执行的,并且在此情况下不会崩溃。
假设你要用future,Async
返回Java Future
。如果你需要返回future结果,就可以用asyncResult
。
4,对象声明
单例模式在一些场景中很有用, 而 Kotlin(继 Scala 之后)使单例声明变得很容易:
有时候,我们需要创建一个对某个类做了轻微改动的类的对象,而不用为之显式声明新的子类。 Java 用匿名内部类处理这种情况。 Kotlin 用对象表达式和对象声明对这个概念稍微概括了下。
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ……
}
val allDataProviders: Collection<DataProvider>
get() = // ……
}
这称为对象声明。并且它总是在 object 关键字后跟一个名称。 就像变量声明一样,对象声明不是一个表达式,不能用在赋值语句的右边。
对象声明的初始化过程是线程安全的。
如需引用该对象,我们直接使用其名称即可:
DataProviderManager.registerDataProvider(……)
这些对象可以有超类型:
object DefaultListener : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { …… } override fun mouseEntered(e: MouseEvent) { …… } }
注意:对象声明不能在局部作用域(即直接嵌套在函数内部),但是它们可以嵌套到其他对象声明或非内部类中。