Kotlin学习笔记

来源:互联网 发布:cf真正刷枪软件 编辑:程序博客网 时间:2024/06/05 08:27


参考学习:http://blog.csdn.net/tangxl2008008/article/category/6340008

Kotlin定义类:

Java一样,Kotlin里面类的声明使用的是关键字class,类的声明包含有三部分:类名,类头,类体,其中,类头和类体是可选的。

 

类成员

1、构造器和初始化块 
2
、成员函数 
3
、属性 
4
、内部类 
5
、对象声明

 

属性和字段:

var和val

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<String>) {

   var customer: Person = Person()

   println(customer.name)   // ABC

   customer.name = "aaa"

   println(customer.name)   //NAME: AAA

}

 

 

延迟初始化 lateinit

对于非空类型的属性是必须初始化的。如果我们希望延迟进行初始化,就可以使用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的接口定义并没有太大出入。

Interface  MyInterface{

    fun bar()

    fun foo() {

      // optionalbody

    }

}

接口的实现

一个类 或 对象 可以实现一个或多个接口。

classChild :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<A>.foo()
    super<B>.foo()
  }
}

 

修饰符

修饰符

Ø  private:同一类或文件(针对包级别定义)中可见

Ø  protected:同private 加子类可见

Ø  internal:在同一个模块中可见(如果声明范围的所有者是可见的)

Ø  public:公共,所有都可见

 

 默认情况下,所有的构造函数都是“public”。

 局部变量,函数和类不能添加可见性修饰符。

 

扩展

类似于C#和Gosu,Kotlin提供了不用继承父类,或者使用像装饰模式这样的设计模式方式来给某个类进行扩展功能(Extensions)。

   扩展是一种静态方式(Extensionsare resolved statically)

   扩展实际没有修改扩展类;当定义一个扩展,并没有增加一个成员到扩展类中,仅仅是在扩展类实例可以通过“.”方式使用该扩展功能。

   调用扩展功能,调用的是实例定义类型对应的扩展,而不是实例的实际类型对应的扩展。

扩展函数

下面方式进行函数扩展:swap为扩展的函数。

fun    MutableList<Int>.swap(index1:Int, index2: Int) {

  val tmp = this[index1] // 'this'correspondsto the list

  this[index1] = this[index2]

  this[index2] = tmp

}

添加扩展函数后,就可以对“MutableList<Int>”实例使用扩展的函数:

 

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() //resultis “member”

 

若是重载(函数名称相同,参数不同)方式扩展一个函数,则根据参数调用到不同的函数:

class C{

   fun foo() { println("member") }

}

 

funC.foo(i: Int) {println("extension") }

val c =C()

c.foo(1)//result is “extension”

 

扩展属性

  Kotlin支持扩展属性,跟扩展函数语法类型,直接在类名后通过“.”方式添加扩展:

val<T>List<T>.lastIndex: Int

    get() =size – 1

注意:

 扩展的属性不是在原类中增加相关属性:

         Ø  扩展属性没有backing field

         Ø  扩展属性不支持默认初始化;需要明确定义“ getters/setters”

友元(伴侣)对象扩展

静态属性(Kotlin中去掉了static,用companion object{ … }方式定义静态函数及属性)的扩展:若一个类中已经有定义“companion object”,则可以对该类“companion object”进行扩展:

class MyClass {

  companion object { }  // willbe called "Companion"

}

fun    MyClass.Companion.foo(){

  // ...

}

//直接使用类名调用扩展

MyClass.foo()

设计扩展的目的(Motivation)

比如在Java中,集合操作的swap 方法的API设计:

这种方式在使用的时候,就感觉不是很流畅,本来需要交换list中的i j两个位置的值,而使用方式确是将[list, i, j]三个参数传入到一个方法中。

   比较流畅的方式应该是list.swap(i, j),本身该操作对应list;但是很多时候API已经设计好或者不能实现所有的可能的方法;这时候可以通过扩展方式来灵活实现需要的功能。

数据类

类似于Java  bean类,只是用来保存数据而不做其他的逻辑操作,在Kotlin中称为“data class”,使用“data”关键字标识:

data class User(val name: String, val age: 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 of age")  // prints "Jane, 35 years of age"

这个好实用

 

 

泛型

classBox<T>(t: T) {

  var value = t

}

 

因为在java中,泛型类型是不可变的,比如:“List<String>”不是“List<Object>”的子类型,而是两个独立的类型;

JAVA中的通配符

泛型使用后类型就固定不变了!

但是有些地方,eg:

// Java

interfaceCollection<E> ... {

  void addAll(Collection<E>items);

}

那么在使用的时候,往“Collection<Object>”中添加“Collection<String>”,该方法就没法使用了;而理论上应该是合法的,String是Object的子类,String实例是可以添加到Object的集合中的。

为了解决上面的问题,Java中使用了类型通配符方式,如“? extends T”表示T 及T的子类参数都可以使用,实现如下:

// Java

interfaceCollection<T> ... {

  void addAll(Collection<? extendsT>items);

}

通配符的上界

   通配符类型参数(wildcard type argument):“?extends T”(T表示通配符的上界),表示该方法可以接收TT的子类参数。意味着可以安全的读“T” (所有的实例都是T的子类)的实例;但不能向其添加元素,因为没法确定添加的实例类型跟定义的类型是否匹配,无法检验这个操作的安全性:

通配符的下界

  通配符类型参数(wildcardtype argument):“? super T”(T表示通配符的下界)。

 

 

Kotlin泛型

Kotlin没有提供相关的类型通配机制,而是通过下面两种方式:

Ø  声明位置变异(declaration-sitevariance)

Ø  类型推测(type projections)

 

位置变异

通过将参数T注解成只能作为返回值而不是作为传入参数;使用out ”关键字标识。

abstract class Source<out T> {
  abstract fun nextT(): T
  //下面的函数编译错误: type parameter T is declared as 'out'
  // abstract fun add(value: T)
}

fun demo(strs: Source<String>) {
  val objects: Source<Any> = strs // This is OK, since T is anout-parameter
  // ...
}

 

类型参数逆变

out修饰符称为异变注解;当在类型参数的声明位置使用它,称之为声明位置变异(declaration-site variance)。跟Java使用位置变异( use-site variance)对比,Java是在使用位置使类型协变。

 

类型推测(Type projections)

(有点难理解,用到再看)

 

 

嵌套类

普通嵌套类

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进行引导出来。

 

枚举类

enumclassDirection {

 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)  

不想创建类,仅仅想用一个表达式搞定

 

 

对象声明(Object declarations)

单例(Singleton)

是一个非常有用的设计模式,在Kotlin中,可以通过下面方式很容易去实现:

object DataProviderManager {  

  fun registerDataProvider(provider: DataProvider) {  

    // ...  

  }  

  val allDataProviders: Collection<DataProvider>  

    get() = // ...  

}  

 

友元对象(Companion Objects)

class MyClass {  

  companion object Factory {  

    fun create(): MyClass = MyClass()  

  }  

}  

val  instance = MyClass.create()  

这个也是通过友元模式实现单例的一个方式

 

 

代理

类代理

代理模式提供一种实现继承的替代方式,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<String>) {  

  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<property name>: <Type> by <expression>,“by”关键字后面的表达式就是代理(delegate);属性的“get()”和“set()”对应代理的“getValue()”和“setValue()”。属性代理不要去实现任何接口,但需要提供“getValue()”和“setValue()”(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.'")  

  }  

 

 属性代理的要求(Property Delegate Requirements

只读属性(read-only,使用val定义)

代理类提供“getValue”函数,参数要求:

Ø  接收者(receiver):第一个参数,必须是属性对应的类或父类型。

Ø  元数据(metadata):第二个参数,必须是“KProperty<*>”或它的父类型。

Ø  该函数,必须返回一个跟属性同类型的值。

 

可变属性(mutable,使用var定义)

代理类的“getValue”函数跟只读属性的一样;另外还需要提供一个“setValue”函数,参数要求:

Ø  接收者(receiver):第一个参数,同“getValue”对应的参数。

Ø  元数据(metadata):第二个参数,同“getValue”对应的参数。

Ø  新值:第三个参数,类型必须跟属性一样或其父类型。

 

标准代理(Standard Delegates)

通过工厂方法提供了一些有用的代理类:

   Lazy

 lazy()”函数接受Lambda表达式 并 返回“Lazy<T>”实例,它可以当做延迟属性的代理实现:当第一次属性执行“get()”(获取属性值,即使用该属性)时,会通过“lazy()”函数执行添加的Lambda表达式并记录返回值;后续再使用该属性时,直接使用记录的值。

 

val lazyValue: String by lazy {  

    println("computed!")  

    "Hello"  

}  

  

fun main(args: Array<String>) {  

    println(lazyValue)  

    println(lazyValue)  

}  

//prints  

//computed!  

//Hello  

//Hello 

 

   可观察方法(Observable)

Delegates.observable()”,包含两个参数:初始化值和 属性值修改的回调handler;每次对属性赋值操作,都会回调该handler方法(在属性赋值后执行),该方法包含三个参数,分别为:属性对象,原值,新值。

class User {  

    var name: String by Delegates.observable("<nomalName>") {  

        prop, old, new ->  

        println("$old -> $new")  

    }  

}  

  

fun main(args: Array<String>) {  

    val user = User()  

    user.name = "first"  

    user.name = "second"  

}  

  

//结果:  

//< nomalName > -> first  

//first -> second  

这个有点好用啊!!直接在代理里面做数据的过滤!

如果需要拦截修改属性值动作并禁止修改,可以使用Delegates.vetoable(),参数跟observable()类似,第二个回调handler需要返回一个Booleantrue表示同意修改,false表示禁止修改;该回调会在属性值修改前调用。

class User {  

    var name: String by Delegates. vetoable ("<nomalName>") {  

        prop, old, new ->  

        println("want modify $old -> $new")  

        false  

}  

}  

  

fun main(args: Array<String>) {  

val user = User()  

println(user.name)  

user.name = "newValue"  

println(user.name)  

}  

  

//结果:  

//< nomalName >  

//want modify < nomalName > -> newValue  

//< nomalName > 

 

Storing Properties in a Map

可以使用map对象作为一个代理属性的代理:

class User(val map: Map<String, Any?>) {  

    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中对应的属性

 

 

 

 

函数

 声明函数

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

 

 默认实参(Default Arguments)

调用函数时,可以给传入的参数添加参数名;当一个函数有多个参数时,非常有用。

fun reformat(str: String,  

             normalizeCase: Boolean = true,  

             upperCaseFirstLetter: Boolean = true,  

             divideByCamelHumps: Boolean = false,  

             wordSeparator: Char = ' ')  {  

...  

l  普通调用方式

reformat(str)  

当该函数的所有参数都没有设置默认值,则需要这样调用:

reformat(str, truetruefalse'_'

只需要传入部分参数情况:

reformat(str, wordSeparator = '_')  

 

返回类型为UNIT

若一个函数不需要返回任何有效值,那么它的返回类型为“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  

}  

 

单表达式函数(Single-Expression functions)

当函数返回一个单表达式的值,可以省略“{ }”直接将该单表达式使用“=”跟在函数定义的后面:

fun double(x: Int): Int = x * 2 

可变参数数量

使用“vararg”关键字修饰参数,即可定义为可变数量参数(一般是最后一个参数):

fun <T> asList(vararg ts: T): List<T> {  

  val result = ArrayList<T>()  

  for (t in ts) // ts is an Array  

    result.add(t)  

    return result  

}  

 

val list = asList(123)  

注意:

一个函数,只能有一个参数可以使用vararg关键字修饰,一般放在最后一个参数。若不是最后一个参数,在使用时,其他的参数可以使用命名参数(Named Arguments)方式;或者函数有一个功能类型参数,可以在函数的后面定义一个Lambda表达式实现。

 vararg参数不是最后一个参数情况  

fun asMultipleList(vararg ts: Int, multiple: Int): List<Int> {  

val resultL = ArrayList<Int>()  

    for(data in ts) {  

        resultL.add(data * multiple)  

     }  

return resultL  

}  

//  

var multiList = asMultipleList(123, multiple=2)  

println(multiList)  

 

后面更有功能类型参数  

fun showInfo(vararg infos: String, show: (info: String) -> Unit) {  

for(info in infos) {  

        show(info)  

    }  

}  

//  

showInfo("info1""info2") {  

println(it)  

 

泛型函数(Generic Functions)

fun <T> singletonList(item: T): List<T> {  

  // ...  

}  

 

高阶函数和Lambda表达式

高阶函数

 将函数作为参数或返回一个函数,称为高阶函数。如“lock()”函数,给对象和函数提供锁功能,获取锁,执行函数,释放锁。

fun <T> lock(lock: Lock, body: () -> T): T {  

  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 <T, R> List<T>.map(transform: (T) -> R): List<R> {

  val result = arrayListOf<R>()  

  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表达式和匿名函数

一个Lambda表达式或一个匿名函数 是 一个函数直接量;即函数本身是没有定义,而是通过立即当做一个函数。如下面的例子:

max(strings, { a, b -> a.length < b.length })  

“max”是一个高阶函数,它的第二个参数需要一个函数。第二个参数值本身就是一个函数,即函数直接量;它等同于下面的函数:

fun compare(a: String, b: String): Boolean = a.length < b.length 

 函数类型(Function Types)

一个函数接收另外一个函数作为参数,需要指定该参数作为函数类型参数。如“max”函数:

fun <T> max(collection: Collection<T>, less: (T, T) -> Boolean): T? {  

  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表达式语法

 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'  

 

匿名函数(Anonymous Functions)

fun(x: Int, y: Int): Int = x + y  

 匿名函数除了省略了函数名称,其他跟一般函数的定义基本类似,函数体可以是一个表达式或其一个代码块

 

 

 

 

不太理解:

1、 标签里面http://my.oschina.net/yuanhonglong/blog/469546


定义自己的标签又引用自己;

 

2、理解为函数指针变量?


 

3、分别是独立定义的静态代码块;


4、已经有了构造函数为什么还要继承下

 

5、怎么理解


0 0
原创粉丝点击