《Kotlin极简教程》第五章 Kotlin面向对象编程(OOP)
来源:互联网 发布:定格动画软件 编辑:程序博客网 时间:2024/06/07 04:08
一个OOP版本的HelloWorld
最新上架!!!
《 Kotlin极简教程》 陈光剑 (机械工业出版社):
https://mp.weixin.qq.com/s/bzRkGSO6T1O2AELM_UqKUQ
/** * Here we have a class with a primary constructor and a member function. * Note that there's no `new` keyword used to create an object. * See http://kotlinlang.org/docs/reference/classes.html#classes */class Greeter(val name: String) { fun greet() { println("Hello, ${name}"); }}fun main(args: Array<String>) { Greeter(args[0]).greet()}
构造函数传参
/** * This example introduces a concept that we call destructuring declarations. * It creates multiple variable at once. Anything can be on the right-hand * side of a destructuring declaration, as long as the required number of component * functions can be called on it. * See http://kotlinlang.org/docs/reference/multi-declarations.html#multi-declarations */fun main(args: Array<String>) { val pair = Pair(1, "one") val (num, name) = pair println("num = $num, name = $name") val triple = Triple(10,"B",10.0) val (a,b,c) = triple println("a=$a, b=$b, c=$c")}class Pair<K, V>(val first: K, val second: V) { operator fun component1(): K { return first } operator fun component2(): V { return second }}class Triple<K,V,T>(val first: K,val second:V,val third:T){ operator fun component1():K{return first} operator fun component2():V{return second} operator fun component3():T{return third}}
运行结果:
num = 1, name = onea=10, b=B, c=10.0
Data Class
We frequently create a class to do nothing but hold data. In such a class some standard functionality is often mechanically derivable from the data. In Kotlin, this is called a data class and is marked as data
data class User(val name: String, val age: Int)
The compiler automatically derives the following members from all properties declared in the primary constructor:
- equals()/ hashCode() pair
- toString() of the form "User(name=John, age=42)",
- componentN() functions corresponding to the properties in their order of declaration,
- copy() function
/** * Data class gets component functions, one for each property declared * in the primary constructor, generated automatically, same for all the * other goodies common for data: toString(), equals(), hashCode() and copy(). * See http://kotlinlang.org/docs/reference/data-classes.html#data-classes */data class User(val name: String, val id: Int)fun getUser(): User { return User("Alex", 1)}fun main(args: Array<String>) { val user = getUser() println("name = ${user.name}, id = ${user.id}") // or val (name, id) = getUser() println("name = $name, id = $id") // or println("name = ${getUser().component1()}, id = ${getUser().component2()}")}
定义接口&实现之
package ch04.ex1_1_1_InterfacesInKotlininterface Clickable { fun click()}class Button : Clickable { override fun click() = println("I was clicked")}fun main(args: Array<String>) { Button().click()}
写pojo bean
package jason.chen.mini_springboot.restful.entityimport java.util.*import javax.persistence.Entityimport javax.persistence.GeneratedValueimport javax.persistence.GenerationTypeimport javax.persistence.Id@Entityclass Customer( val firstName: String, val lastName: String, val gmtCreated: Date, val gmtModified: Date, val isDeleted: Int, //1 Yes 0 No val deletedDate:Date, @Id @GeneratedValue(strategy = GenerationType.AUTO) val id: Long = -1) { override fun toString(): String { return "Customer(firstName='$firstName', lastName='$lastName', gmtCreated=$gmtCreated, gmtModified=$gmtModified, isDeleted=$isDeleted, deletedDate=$deletedDate, id=$id)" }}data class Shop(val name: String, val customers: List<Customer>)data class Customer(val name: String, val city: City, val orders: List<Order>) { override fun toString() = "$name from ${city.name}"}data class Order(val products: List<Product>, val isDelivered: Boolean)data class Product(val name: String, val price: Double) { override fun toString() = "'$name' for $price"}data class City(val name: String) { override fun toString() = name}
定一个Rectangle对象
package geometry.shapesimport java.util.Randomclass Rectangle(val height: Int, val width: Int) { val isSquare: Boolean get() = height == width}fun createRandomRectangle(): Rectangle { val random = Random() return Rectangle(random.nextInt(), random.nextInt())}
封装个日期工具类
package jason.chen.mini_springboot.restful.utilsimport java.text.SimpleDateFormatimport java.util.*/** * Created by jack on 2017/3/11. * @author jack * @date 2017/03/11 * * val date = Date() date + 1 //后一天 date - 1 //前一天 date + Month(2) //后2月 date - Year(3) //前3年 date++ //本月的最后一天 date-- //本月的第一天 取年月日时分秒 date[0] date[1] date[2] 。。。 //日期比较 if( date1 > date2){ } */enum class DateOptUnit { YEAR,MONTH,DATE; fun parseType():Int{ var value = Calendar.DATE when(this){ YEAR -> value = Calendar.DATE MONTH -> value = Calendar.MONTH DATE -> value = Calendar.DATE } return value }}data class DateOperator(val unit :DateOptUnit,val value: Int)fun Any.year(value:Int):DateOperator { return DateOperator(DateOptUnit.YEAR,value)}fun Any.month(value:Int):DateOperator { return DateOperator(DateOptUnit.MONTH,value)}fun Any.day(value:Int):DateOperator { return DateOperator(DateOptUnit.DATE,value)}/** * date+1 * 往后的几天 */operator fun Date.plus(nextVal:Int):Date{ val calendar = GregorianCalendar() calendar.time = this calendar.add(Calendar.DATE, nextVal) return calendar.time}/** * date-1 */operator fun Date.minus(nextVal:Int):Date{ val calendar = GregorianCalendar() calendar.time = this calendar.add(Calendar.DATE, nextVal*-1) return calendar.time}/** * date+year(3) * 往后的几天 */operator fun Date.plus(nextVal:DateOperator):Date{ val calendar = GregorianCalendar() calendar.time = this calendar.add(nextVal.unit.parseType(), nextVal.value) return calendar.time}/** * date-month(4) */operator fun Date.minus(nextVal:DateOperator):Date{ val calendar = GregorianCalendar() calendar.time = this calendar.add(nextVal.unit.parseType(), nextVal.value*-1) return calendar.time}/** * 得到月末 */operator fun Date.inc():Date { val calendar = GregorianCalendar() calendar.time = this calendar.add(Calendar.MONTH, 1); calendar.set(Calendar.DAY_OF_MONTH, 0); return calendar.time}/** * 得到月初 */operator fun Date.dec():Date { val calendar = GregorianCalendar() calendar.time = this calendar.set(Calendar.DAY_OF_MONTH, 1) return calendar.time}/** * 取 年月日时分秒 0 - 5 * 例如 2015-12-21 22:15:56 * date[0]:2015 date[1]:12 date[2]:21 */operator fun Date.get(position:Int):Int { val calendar = GregorianCalendar() calendar.time = this var value = 0 when(position) { 0 -> value = calendar.get(Calendar.YEAR) 1 -> value = calendar.get(Calendar.MONTH)+1 2 -> value = calendar.get(Calendar.DAY_OF_MONTH) 3 -> value = calendar.get(Calendar.HOUR) 4 -> value = calendar.get(Calendar.MINUTE) 5 -> value = calendar.get(Calendar.SECOND) } return value}/** * 比较2个日期 * if(date1 > date2) { * } */operator fun Date.compareTo(compareDate : Date):Int { return (time - compareDate.time).toInt()}/** * 日期转化为字符串 */fun Date.stringFormat(formatType:String):String{ return SimpleDateFormat(formatType).format(this)}
示例代码1
/** * This example introduces a concept that we call destructuring declarations. * It creates multiple variable at once. Anything can be on the right-hand * side of a destructuring declaration, as long as the required number of component * functions can be called on it. * See http://kotlinlang.org/docs/reference/multi-declarations.html#multi-declarations */fun main(args: Array<String>) { val pair = Pair(1, "one") val (num, name) = pair println("num = $num, name = $name")}class Pair<K, V>(val first: K, val second: V) { operator fun component1(): K { return first } operator fun component2(): V { return second }}
示例代码2
/** * Data class gets component functions, one for each property declared * in the primary constructor, generated automatically, same for all the * other goodies common for data: toString(), equals(), hashCode() and copy(). * See http://kotlinlang.org/docs/reference/data-classes.html#data-classes */data class User(val name: String, val id: Int)fun getUser(): User { return User("Alex", 1)}fun main(args: Array<String>) { val user = getUser() println("name = ${user.name}, id = ${user.id}") // or val (name, id) = getUser() println("name = $name, id = $id") // or println("name = ${getUser().component1()}, id = ${getUser().component2()}")}
类和继承
类
类声明Kotlin使用关键字*class *{:.keyword}
class Invoice {}
这个类声明被花括号包围,包括类名、类头(指定其类型参数,主构造函数等)和这个类的主干。类头和主干都是可选的;
如果这个类没有主干,花括号可以被省略。
class Empty
构造
在Kotlin中的类可以有主构造函数和一个或多个二级构造函数。主构造函数是类头的一部分:它跟在这个类名后面(和可选的类型参数)
class Person constructor(firstName: String) {}
如果这个主构造函数没有任何注解或者可见的修饰符,这个constructor{: .keyword }关键字可以被省略
class Person(firstName: String) {}
这个主构造函数不能包含任何的代码。初始化的代码可以被放置在initializer blocks(初始的模块),以init为前缀作为关键字{:.keyword}
class Customer(name: String) { init { logger.info("Customer initialized with value ${name}") }}
请注意,主构造的参数可以在初始化模块中使用。它们也可以在类体内声明初始化的属性:
class Customer(name: String) { val customerKey = name.toUpperCase()}
事实上,声明属性和初始化主构造函数,Kotlin有简洁的语法:
class Person(val firstName: String, val lastName: String, var age: Int) { // ...}
与普通属性一样,主构造函数中声明的属性可以是可变的或者是只读的
If the constructor has annotations or visibility modifiers, the constructor{: .keyword } keyword is required, and
the modifiers go before it:
如果构造函数有注解或可见性修饰符,这个constructor{: .keyword }需要被关键字修饰。
class Customer public inject constructor(name: String) { ... }
更多请查看Visibility Modifiers
扩展构造函数
类也可以拥有被称为"二级构造函数"(为了实现Kotlin向Java一样拥有多个构造函数),通常被加上前缀"constructor"
class Person { constructor(parent: Person) { parent.children.add(this) }}
如果类有一个主构造函数,每个二级构造函数需要委托给主构造函数,直接或间接地通过另一个二级函数。
委托到另一个使用同一个类的构造函数用this{: .keyword }关键字
class Person(val name: String) { constructor(name: String, parent: Person) : this(name) { parent.children.add(this) }}
如果一个非抽象类没有声明任何构造函数(原发性或继发性),这将有一个生成的主构造函数不带参数。构造函数的可见性是public。如果你不希望你的类有一个公共构造函数,你需要声明与非缺省可见一个空的主构造函数:
class DontCreateMe private constructor () {}
注意在JVM上,如果所有的主构造函数的参数有默认值,编译器会产生一个额外的参数的构造函数,将使用默认值。
这使得更易于使用kotlin与通过参数构造函数创建类的实例,如使用Jackson或JPA库的时候。class Customer(val customerName: String = "")
{:.info}
创建类的实例
要创建一个类的实例,我们调用构造函数,就好像它是普通的函数:
val invoice = Invoice()val customer = Customer("Joe Smith")
注意Kotlin不能有“new”关键字
类成员
类可以包括
- 构造和初始化模块
- 函数
- 属性
- 匿名和内部类
- 对象声明
继承
在Kotlin所有的类中都有一个共同的父类Any
,这是一个默认的父类且没有父类型声明:
class Example // Implicitly inherits from Any
Any
不属于java.lang.Object
;特别是,它并没有任何其他任何成员,甚至连equals()
,hashCode()
和toString()
都没有。
请参阅Java的互操作性更多的细节部分。
要声明一个明确的父类,我们把类型放到类头冒号之后:
open class Base(p: Int)class Derived(p: Int) : Base(p)
如上所见,父类可以(并且必须)在声明继承的地方,用原始构造函数初始化。
如果类没有主构造,那么每个次级构造函数初始化基本类型
使用super{:.keyword}关键字,或委托给另一个构造函数做到这一点。
注意,在这种情况下,不同的二级构造函数可以调用基类型的不同的构造:
class MyView : View { constructor(ctx: Context) : super(ctx) { } constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) { }}
父类上的open{:.keyword}标注可以理解为Java中final{:.keyword}的反面,它允许其他他类
从这个类中继承。默认情况下,在Kotlin所有的类都是final,
对应于 Effective Java
书中的17条:设计并显示标注继承,否则就禁止它。
覆盖成员
我们之前提到过,Kotlin力求清晰显式。不像Java中,Kotlin需要明确的
标注覆盖的成员(我们称之为open)和重写的函数。(继承父类并覆盖父类函数时,Kotlin要求父类必须有open标注,被覆盖的函数必须有open标注,并且子类的函数必须加override标注。):
open class Base { open fun v() {} fun nv() {}}class Derived() : Base() { override fun v() {}}
Derived.v()函数上必须加上override标注。如果没写,编译器将会报错。
如果父类的这个函数没有标注open,则子类中不允许定义同名函数,不论加不加override。
在一个final类中(即没有声明open的类),函数上也不允许加open标注。
成员标记为override{:.keyword}的本身是开放的,也就是说,它可以在子类中重写。如果你想禁止重写的,使用final{:.keyword}关键字:
open class AnotherDerived() : Base() { final override fun v() {}}
等等!!这样我怎么hack我的库?
我们这样设计继承和覆盖的方式(类和成员默认final),会让人很难继承第三方的类,因此很难进行hack。
我们认为这不是一个劣势,原因如下:
- 最佳实践已经表明不应该使用这些hacks
- 其他的有类似机制的语言(C++, C#)已经证明是成功的
- 如果人们实在想hack,仍然有办法:比如某些情况下可以使用Java进行hack,再用Kotlin调用;或者使用面向切面的框架(Aspect)。(请参阅Java的互操作)
重写的规则
在Kotlin中,实现继承的调用通过以下规则:
如果一个类继承父类成员的多种实现方法,可以直接在子类中引用,
它必须重写这个成员,并提供其自己的实现(当然也可以使用父类的)。
为了表示从中继承的实现而采取的父类型,我们使用super{:.keyword}在尖括号,如规范的父名super<Base>
:
open class A { open fun f() { print("A") } fun a() { print("a") }}interface B { fun f() { print("B") } // interface members are 'open' by default fun b() { print("b") }}class C() : A(), B { // The compiler requires f() to be overridden: override fun f() { super<A>.f() // call to A.f() super<B>.f() // call to B.f() }}
类C同时继承A和B是可以的,而且我们在调用a()和b()函数时没有任何问题,因为他们在C的基类中只有一个实现。
但是f()函数则在A,B中都有实现,所以我们必须在C中覆盖f(),并且提供我们的实现以消除歧义。
抽象类
类和其中的某些实现可以声明为abstract{:.keyword}。
抽象成员在本类中可以不用实现。。
因此,当一些子类继承一个抽象的成员,它并不算是一个实现:
abstract class A { abstract fun f()}interface B { open fun f() { print("B") }}class C() : A(), B { // We are not required to override f()}
Note that we do not need to annotate an abstract class or function with open – it goes without saying.
We can override a non-abstract open member with an abstract one
需要注意的是,我们并不需要标注一个抽象类或者函数为open - 因为这不言而喻。
我们可以重写一个open非抽象成员使之为抽象的。
open class Base { open fun f() {}}abstract class Derived : Base() { override abstract fun f()}
同伴对象
在Kotlin中,不像Java或C#,类没有静态方法。在大多数情况下,它建议简单地使用包级函数。
如果你需要写一个可以调用的函数,而不依赖一个类的实例,但需要访问的内部一个类(例如,一个工厂方法),你可以写为[对象声明](object_declarations.html)中的一员里面的那个类。
更具体地讲,如果你声明一个同伴对象在你的的类中,
你就可以在Java/ C#中调用与它的成员方法相同的语法的静态方法,只使用类名作为一个修饰语。
代码示例
java代码:
package com.restfeel.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Profile;import org.springframework.context.annotation.PropertySource;import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;/** * Created by santoshm1 on 04/06/14. * * Adds support for runtime property files. Run with -Dspring.profiles.active={production,default,development,test} * defaults to development. */@Configuration@PropertySource(value = {"classpath:common.properties"})public class PropertyConfig { public PropertyConfig() {} @Bean public static PropertySourcesPlaceholderConfigurer myPropertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } /** * Properties to support the 'test' mode of operation. */ @Configuration @Profile({"devlopment", "default"}) @PropertySource(value = {"classpath:env-development.properties"}) static class Dev { } /** * Properties to support the 'test' mode of operation. */ @Configuration @Profile("test") @PropertySource(value = {"classpath:env-test.properties"}) static class Test { } /** * Properties to support the 'production' mode of operation. */ @Configuration @Profile("production") @PropertySource(value = {"classpath:env-production.properties"}) static class Production { // Define additional beans for this profile here }}
对应的kotlin代码:
package com.restfeel.configimport org.springframework.context.annotation.Beanimport org.springframework.context.annotation.Configurationimport org.springframework.context.annotation.Profileimport org.springframework.context.annotation.PropertySourceimport org.springframework.context.support.PropertySourcesPlaceholderConfigurer/** * Created by jack on 2017/3/29. */@Configuration@PropertySource(value = *arrayOf("classpath:common.properties"))class ApplicationConfig { @Bean fun myPropertySourcesPlaceholderConfigurer(): PropertySourcesPlaceholderConfigurer { return PropertySourcesPlaceholderConfigurer(); } //静态类,伴生对象 companion object { /** * Properties to support the 'test' mode of operation. */ @Configuration @Profile(*arrayOf("devlopment", "default")) @PropertySource(value = *arrayOf("classpath:env-development.properties")) class Dev { } /** * Properties to support the 'test' mode of operation. */ @Configuration @Profile("test") @PropertySource(value = *arrayOf("classpath:env-test.properties")) class Test { } /** * Properties to support the 'production' mode of operation. */ @Configuration @Profile("production") @PropertySource(value = *arrayOf("classpath:env-production.properties")) class Production { // Define additional beans for this profile here } }}
参考文档:
https://github.com/kymjs/KotlinDoc-cn/blob/master/unit3/ClassesInheritance.md
- 《Kotlin极简教程》第五章 Kotlin面向对象编程(OOP)
- 《Kotlin 程序设计》第五章 Kotlin 面向对象编程(OOP)
- Kotlin极简教程:第7章 面向对象编程
- 《Kotlin极简教程》第六章 Kotlin函数式编程(FP)
- 《Groovy极简教程》第10章 Groovy面向对象编程(OOP)
- 《Kotin 极简教程》第7章 面向对象编程(OOP)(1)
- 《Kotin 极简教程》第7章 面向对象编程(OOP)(2)
- 《Kotlin极简教程》第三章 Kotlin基本数据类型
- 《Kotlin极简教程》第四章 Kotlin基础语法
- 《Kotlin极简教程》第1章 Kotlin简介
- 《Kotlin极简教程》第3章 Kotlin语言基础
- 《Kotlin极简教程》第一章 Kotlin简介
- Kotlin极简教程(第一章 Kotlin简介)
- Kotlin极简教程
- 《Kotlin极简教程》
- Kotlin中的面向对象(一)
- Kotlin中的面向对象(二)
- 面向对象编程(OOP)面向对象编程(OOP)
- 《Kotlin极简教程》第一章 Kotlin简介
- linux shell脚本执行错误:bad substitution
- 《Kotlin极简教程》第二章 Hello,World 函数
- 《Kotlin极简教程》第三章 Kotlin基本数据类型
- 《Kotlin极简教程》第六章 Kotlin函数式编程(FP)
- 《Kotlin极简教程》第五章 Kotlin面向对象编程(OOP)
- 《Kotlin极简教程》第四章 Kotlin基础语法
- 《Kotlin极简教程》第七章 Kotlin 集成 Springboot开发WebApp
- Linux之ps命令
- 测试思考拾叶集
- 测试金字塔 TestPyramid
- A bug's life(带权并查集)
- 并发编程之CAS(Compare and Swap)原理
- ReentrantLock实现原理