博客地址:http://blog.csdn.net/tangxl2008008/article/category/6340008
http://kotlinlang.org/docs/reference/object-declarations.html
“?”:可不做处理返回值为 null或配合?:做空判断处理;
“!!”:字段后加!!像Java一样抛出空异常;
eg:
//类型后面加?表示可为空 varage: String? = "23"
//抛出空指针异常 val ages =age!!.toInt()
//不做处理返回 null valages1 = age?.toInt()
//age为空返回-1 valages2 = age?.toInt() ?: -1
var是允许有getter 和 setter方法,如果变量是val声明的,它类似于Java中的final,所以如果以val声明就不允许有setter方法。
示例:
class Person {
var name: String = "abc"
get() = field.toUpperCase()
set(value){
field = "Name: $value"
}
}
fun main(args: Array
var customer: Person = Person()
println(customer.name) // ABC
customer.name = "aaa"
println(customer.name) //NAME:AAA
}
对于非空类型的属性是必须初始化的。如果我们希望延迟进行初始化,就可以使用lateinit关键字了。
lateinit只能在不可null的对象上使用,比须为var,不能为primitives(Int、Float之类)。
public class MyTest{
lateinit var subject:TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // dereference directly
}
}
跟Java一样,Kotlin里面类的声明使用的是关键字class,类的声明包含有三部分:类名,类头,类体,其中,类头和类体是可选的。
类成员:
1、构造器和初始化块
2、成员函数
3、属性
4、内部类
5、对象声明
Kotlin中没有static修饰的静态属性,函数,类。我们可以使用companion修饰的伴随对象来实现静态
是一个非常有用的设计模式,在Kotlin中,可以通过下面方式很容易去实现:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
val instance = MyClass.create()
这个也是通过友元模式实现单例的一个方式
类似于Java bean类,只是用来保存数据而不做其他的逻辑操作,在Kotlin中称为“data class”,使用“data”关键字标识:
data class User(val name: String, valage: Int)
编译器会自动根据主构造器定义的参数属性生成下面的函数:
Ø equals()/hashCode()
Ø toString() 格式:"User(name=John,age=42)"
Ø componentN() 对应按照定义的顺序排列的所有属性
Ø copy()
注:若这些函数有在类中显式定义或继承得到,不会再生成默认的实现。
为了保持代码的一致性和有效性,数据类(DataClasses)需要满足下面几点:
1) 主构造函数至少有一个参数。
2) 主构造函数的参数需要标记为“val”或“var”。
3) 不能使用“abstract”“open”“sealed ”“inner”修饰。
4) 一般不要去继承其他类(可以实现接口)
有时候,已有一个实例,需要生成一个新的实例,该实例只有部分参数值不同,其他参数不变;这就是“copy()”函数的目的,如前面的User类的copy实现:
val jack =User(name = "Jack", age= 1)
val olderJack = jack.copy(age = 2)
可以将数据类中的参数解构到一个数据组中;根据数据类参数的定义顺序依次解构到数组主的参数中,如:
val jane =User("Jane", 35)
val (name, age) =jane
println("$name, $age years ofage") // prints "Jane, 35 years of age"
这个好实用
classBox
var value = t
}
因为在java中,泛型类型是不可变的,比如:“List
泛型使用后类型就固定不变了!
但是有些地方,eg:
// Java
interface Collection
void addAll(Collection
}
那么在使用的时候,往“Collection
为了解决上面的问题,Java中使用了类型通配符方式,如“? extends T”表示T 及T的子类参数都可以使用,实现如下:
// Java
interface Collection
void addAll(Collection extendsT>items);
}
通配符类型参数(wildcard type argument):“? extends T”(T表示通配符的上界),表示该方法可以接收T及T的子类参数。意味着可以安全的读“T” (所有的实例都是T的子类)的实例;但不能向其添加元素,因为没法确定添加的实例类型跟定义的类型是否匹配,无法检验这个操作的安全性:
通配符类型参数(wildcard type argument):“? super T”(T表示通配符的下界)。
通过将参数T注解成只能作为返回值而不是作为传入参数;使用“out ”关键字标识。
abstract class Source
abstract fun nextT(): T
//下面的函数编译错误: type parameter T is declared as 'out'
// abstract fun add(value: T)
}
fun demo(strs: Source
val objects: Source
// ...
}
“out”修饰符称为异变注解;当在类型参数的声明位置使用它,称之为声明位置变异(declaration-site variance)。跟Java使用位置变异( use-sitevariance)对比,Java是在使用位置使类型协变。
(有点难理解,用到再看)
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}
val demo =Outer.Nested().foo() // == 2
window.addMouseListener(object:MouseAdapter(){
override fun mouseClicked(e:MouseEvent) {
// ...
}
override fun mouseEntered(e:MouseEvent) {
// ...
}
})
记得用object进行引导出来。
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
需要修改一个类的部分功能,可以不通过显式实现一个该类的子类方式来实现。在Java中,通过匿名内部类来实现;
在Kotlin中,概括为对象表达式和对象声明
(object expressions and object declarations)。
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
})
就是在不想创造新对象的时候用object来创建一个临时的对象。
类似于Java的匿名内部类,对象表达式也可以访问闭合范围内局部变量(跟Java不同,变量不用声明为 final):
fun countClicks(window: JComponent) {
var clickCount = 0
var enterCount = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}
override fun mouseEntered(e: MouseEvent) {
enterCount++
}
})
// ...
}
有时,只需要一个对象表达式,不想继承任何的父类型,实现如下:
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
不想创建类,仅仅想用一个表达式搞定
var testObject = object {
fun registerDataProvider(provider: DataProvider) {
// ...
}
val allDataProviders: Collection
get() = // ...
}
这里用object声明,创建一个对象给testObject。
在Kotlin中,使用“fun”关键字声明函数:
fun double(x: Int): Int {
}
(我不知道干嘛用的)
函数满足下面几个条件,可以使用中缀方式调用:
Ø 为成员函数或扩展函数
Ø 有且只有一个参数
Ø 使用“infix”关键字修饰
// Define extension to Int
infix fun Int.shl(x: Int): Int {
...
}
// call extension function using infix notation
1 shl 2
// is the same as
1.shl(2)
调用函数时,可以给传入的参数添加参数名;当一个函数有多个参数时,非常有用。
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
...
}
普通调用方式
reformat(str)
当该函数的所有参数都没有设置默认值,则需要这样调用:
reformat(str, true, true, false, '_')
只需要传入部分参数情况:
reformat(str, wordSeparator = '_')
若一个函数不需要返回任何有效值,那么它的返回类型为“Unit”。“Unit
”类型只有一个值,即“Unit
”。另,可以不显示返回Unit:
fun printHello(name: String?): Unit {
if (name != null)
println("Hello ${name}")
else
println("Hi there!")
// `return Unit` or `return` is optional
}
当函数返回一个单表达式的值,可以省略“{ }”直接将该单表达式使用“=”跟在函数定义的后面:
fun double(x: Int): Int = x * 2
使用“vararg”关键字修饰参数,即可定义为可变数量参数(一般是最后一个参数):
fun
val result = ArrayList
for (t in ts) // ts is an Array
result.add(t)
return result
}
val list = asList(1, 2, 3)
注意:
一个函数,只能有一个参数可以使用“vararg”关键字修饰,一般放在最后一个参数。若不是最后一个参数,在使用时,其他的参数可以使用命名参数(Named Arguments)方式;或者函数有一个功能类型参数,可以在函数的后面定义一个Lambda表达式实现。
vararg参数不是最后一个参数情况
fun asMultipleList(vararg ts: Int, multiple: Int): List
val resultL = ArrayList
for(data in ts) {
resultL.add(data * multiple)
}
return resultL
}
var multiList = asMultipleList(1, 2, 3, multiple=2)
println(multiList)
后面更有功能类型参数
fun showInfo(vararg infos: String, show: (info: String) -> Unit) {
for(info in infos) {
show(info)
}
}
showInfo("info1", "info2") {
println(it)
}
fun
// ...
}
将函数作为参数或返回一个函数,称为高阶函数。如“lock()”函数,给对象和函数提供锁功能,获取锁,执行函数,释放锁。
fun
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
该函数的参数“body”是一个函数类型:“() -> T”,表示为一个函数,没有入参,返回一个“T”的值。
fun toBeSynchronized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchronized)
另外,也使用使用Lambda表示方式:
val result = lock(lock, { sharedResource.operation() })
Lambda表达式详细内容见“Lambda表达式”部分,这里先简要概述:
Ø Lambda表达一般使用“{ }”包围。
Ø 它的参数(如果有的话)在“->”前定义,参数类型可能是省略的。
Ø 函数体跟在“->”后面。
在Kotlin中,若函数最后一个参数为函数类型,调用时,该参数可以放到函数“()”的外面:
fun
val result = arrayListOf
for (item in this)
result.add(transform(item))
return result
}
可以这样调用,当只有Lambda表达式参数时,调用函数时后面的“()”也可以省略:
var ints = asList(1, 2, 3, 4)
val doubledList = ints.map { it -> it * 2 }
若函数参数对应的函数只有一个参数,在使用时,可以省略参数定义,直接使用“it”代替参数:
ints.map { it * 2 }
一个Lambda表达式或一个匿名函数是 一个函数直接量;即函数本身是没有定义,而是通过立即当做一个函数。如下面的例子:
max(strings, { a, b -> a.length < b.length })
“max”是一个高阶函数,它的第二个参数需要一个函数。第二个参数值本身就是一个函数,即函数直接量;它等同于下面的函数:
fun compare(a: String, b: String): Boolean = a.length < b.length
一个函数接收另外一个函数作为参数,需要指定该参数作为函数类型参数。如“max”函数:
fun
var max: T? = null
for (it in collection)
if (max == null || less(max, it))
max = it
return max
}
参数“less”的类型为“(T, T) -> Boolean”,即表示入参为两个类型为“T”的参数,返回一个“Boolean”值的函数;true表示第一个值小于第二个值。
第4行将“less”当做一个函数使用。
一个函数类型可以通过上面方式实现,若想记录每个参数的意义,也可以定义成一个变量方式:
val compare: (x: T, y: T) -> Int = ...
Lambda表达式句法形式 就是 一个函数类型文本,如:
val sum = { x: Int, y: Int -> x + y }
一个Lambda表达式通常使用“{ }”包围,参数是定义在“()”内,可以添加类型注解,实体部分跟在“->”后面;下面为一个去掉所有的可选注解的Lambda表达:
val sum: (Int, Int) -> Int = { x, y -> x + y }
经常情况下面,Lambda表达式只有一个参数,可以不定义该参数,注解使用“it”关键字代替:
ints.filter { it > 0 } // this literal is of type '(it: Int) -> Boolean'
fun(x: Int, y: Int): Int = x + y
匿名函数除了省略了函数名称,其他跟一般函数的定义基本类似,函数体可以是一个表达式或其一个代码块
和java的接口定义并没有太大出入。
Interface MyInterface{
fun bar()
fun foo() {
// optionalbody
}
}
一个类或 对象 可以实现一个或多个接口。
Class Child:MyInterface {
override fun bar() {
// body
}
}
(这个有点厉害,和java不一样)
可以在接口中声明属性,属性必须是抽象的或提供访问实现
Interface MyInterface{
val property: Int //abstract
valpropertyWithImplementation: String
get() = "foo"
fun foo() {
print(property)
}
}
class Child :MyInterface {
override val property:Int = 29
}
多个继承(父类)或实现(接口)后,同一个方法在不同的父类中有多个不同的实现版本;但一个类都继承或实现该类后,同一个方法会有多个实现版本,就会造成重写冲突。所以kotlin里面我们要指定谁是谁的方法。
interface A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super.foo()
super.foo()
}
}
Ø private:同一类或文件(针对包级别定义)中可见
Ø protected:同private 加子类可见
Ø internal:在同一个模块中可见(如果声明范围的所有者是可见的)
Ø public:公共,所有都可见
默认情况下,所有的构造函数都是“public”。
局部变量,函数和类不能添加可见性修饰符。
类似于C#和Gosu,Kotlin提供了不用继承父类,或者使用像装饰模式这样的设计模式方式来给某个类进行扩展功能(Extensions)。
扩展是一种静态方式(Extensions are resolved statically)
扩展实际没有修改扩展类;当定义一个扩展,并没有增加一个成员到扩展类中,仅仅是在扩展类实例可以通过“.”方式使用该扩展功能。
调用扩展功能,调用的是实例定义类型对应的扩展,而不是实例的实际类型对应的扩展。
下面方式进行函数扩展:swap为扩展的函数。
fun MutableList
val tmp = this[index1] // 'this'correspondsto the list
this[index1] = this[index2]
this[index2] = tmp
}
添加扩展函数后,就可以对“MutableList
val tempM = mutableListOf(1, 2, 3) //[1,2,3]
tempM.swap(0,1) //[2, 1, 3]
类有一个函数,再给该类定义一个相同名称的扩展函数,则该类的实例调用的为类原有的函数。
class C {
fun foo() {println("member") }
}
fun C.foo() {println("extension")}
val c = C()
c.foo() //result is “member”
若是重载(函数名称相同,参数不同)方式扩展一个函数,则根据参数调用到不同的函数:
class C {
fun foo() {println("member") }
}
fun C.foo(i: Int){println("extension") }
val c = C()
c.foo(1) //result is “extension”
静态属性(Kotlin中去掉了static,用companion object{ … }方式定义静态函数及属性)的扩展:
若一个类中已经有定义“companion object”,则可以对该类“companion object”进行扩展:
class MyClass {
companion object { } // willbe called "Companion"
}
fun MyClass.Companion.foo(){
// ...
}
//直接使用类名调用扩展
MyClass.foo()
比如在Java中,集合操作的swap 方法的API设计:
这种方式在使用的时候,就感觉不是很流畅,本来需要交换list中的i j两个位置的值,而使用方式确是将[list, i, j]三个参数传入到一个方法中。
比较流畅的方式应该是list. swap(i, j),本身该操作对应list;但是很多时候API已经设计好或者不能实现所有的可能的方法;这时候可以通过扩展方式来灵活实现需要的功能。
代理模式提供一种实现继承的替代方式,Kotlin原生就支持代理模块。
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main(args: Array
val b = BaseImpl(10)
Derived(b).print() // prints 10
}
通过“by”关键字,将“b”实例存储到Derived对象中,编译器会生成“Base”接口的所有方法,使用“b”的实现。
对于很多公用属性,尽管在每次需要的时候可以通过手动实现;更好的方式是一次实现多次使用,并放到一个库(library)。
Ø 延迟属性(lazyproperties):只有第一次访问时才会计算值。
Ø 观察属性(observableproperties):当该属性发生改变时,会通知监听者。
Ø map中存储属性,不是在单独的字段中。
为了满足上面几种情况,提供了代理属性(delegated properties):
class Example {
var p: String by Delegate()
}
语法:val/var
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name} in $thisRef.'")
}
}
只读属性(read-only,使用val定义)
代理类提供“getValue”函数,参数要求:
Ø 接收者(receiver):第一个参数,必须是属性对应的类或父类型。
Ø 元数据(metadata):第二个参数,必须是“KProperty<*>”或它的父类型。
Ø 该函数,必须返回一个跟属性同类型的值。
可变属性(mutable,使用var定义)
代理类的“getValue”函数跟只读属性的一样;另外还需要提供一个“setValue”函数,参数要求:
Ø 接收者(receiver):第一个参数,同“getValue”对应的参数。
Ø 元数据(metadata):第二个参数,同“getValue”对应的参数。
Ø 新值:第三个参数,类型必须跟属性一样或其父类型。
通过工厂方法提供了一些有用的代理类:
“lazy()”函数接受Lambda表达式 并 返回“Lazy
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main(args: Array
println(lazyValue)
println(lazyValue)
}
//prints:
//computed!
//Hello
//Hello
“Delegates.observable()”,包含两个参数:初始化值和 属性值修改的回调handler;每次对属性赋值操作,都会回调该handler方法(在属性赋值后执行),该方法包含三个参数,分别为:属性对象,原值,新值。
class User {
var name: String by Delegates.observable("
prop, old, new ->
println("$old -> $new")
}
}
fun main(args: Array
val user = User()
user.name = "first"
user.name = "second"
}
//结果:
//< nomalName > -> first
//first -> second
这个有点好用啊!!直接在代理里面做数据的过滤!
如果需要拦截修改属性值动作并禁止修改,可以使用“Delegates.vetoable()”,参数跟“observable()”类似,第二个回调handler需要返回一个Boolean,true表示同意修改,false表示禁止修改;该回调会在属性值修改前调用。
class User {
var name: String by Delegates. vetoable ("
prop, old, new ->
println("want modify $old -> $new")
false
}
}
fun main(args: Array
val user = User()
println(user.name)
user.name = "newValue"
println(user.name)
}
//结果:
//< nomalName >
//want modify < nomalName > -> newValue
//< nomalName >
可以使用map对象作为一个代理属性的代理:
class User(val map: Map
val name: String by map
val age: Int by map
}
//
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
//
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
注:若属性定义为“var”,需要使用“MutableMap ”代替只读的map。
这样,我们可以通过map直接去初始话user中对应的属性
1、 标签里面http://my.oschina.net/yuanhonglong/blog/469546
定义自己的标签又引用自己;
2、理解为函数指针变量?
3、分别是独立定义的静态代码块;
4、已经有了构造函数为什么还要继承下
5、怎么理解