Kotlin支持面向对象编程(OOP)以及函数式编程。 面向对象编程基于对象和类。
Kotlin还支持OOP语言的支柱,如:封装,继承和多态。
Kotlin类与Java类基本类似,类是具有共同属性的对象的蓝图。 Kotlin类使用关键字class声明。 Kotlin类有一个类头,它指定了它的类型参数,构造函数等,以及由花括号包围的类体。
class Account {
var acc_no: Int = 0
var name: String? = null
var amount: Float = 0f
fun insert(ac: Int,n: String, am: Float ) {
acc_no=ac
name=n
amount=am
println("账号: ${acc_no} ,账户名 :${name},总金额 :${amount}")
}
fun deposit() {
//deposite code
}
fun withdraw() {
// withdraw code
}
fun checkBalance() {
//balance check code
}
}
Account类有三个属性acc_no,name,amount和三个成员函数:deposit(),withdraw(),checkBalance()。
在Kotlin中,属性必须初始化或声明为抽象。 在上面的类中,属性acc_no初始化为0,name为null,amount为0f。
在引用的时候:
fun main(args: Array<String>){
// Account()
var acc= Account()
acc.insert(832345,"Maxsu",1000f) // 访问成员函数
println("${acc.name}") // 访问类属性
}
嵌套类和内部类
嵌套类
嵌套类是在另一个类中创建的类。 在Kotlin中,嵌套类默认是静态的,因此可以在不创建类对象的情况下访问其数据成员和成员函数。 嵌套类无法访问外部类的数据成员。
示例代码:
class outerClass{
private var name: String = "Ashu"
class nestedClass{
var description: String = "code inside nested class"
private var id: Int = 101
fun foo(){
// print("name is ${name}") // cannot access the outer class member
println("Id is ${id}")
}
}
}
fun main(args: Array<String>){
// nested class must be initialize
println(outerClass.nestedClass().description) // accessing property
var obj = outerClass.nestedClass() // object creation
obj.foo() // access member function
}
Kotlin内部类
内部类是一个使用关键字inner在一个类中创建的类。 换句话说,可以说标记为inner的嵌套类称为内部类。
内部类不能在接口或非内部嵌套类中声明。内部类优于嵌套类的优点是,
即使它是私有的,它也能够访问外部类的成员。 内部类保持对外部类的对象的引用。
class Outer {
private val bar: Int = 1
var v = "成员属性"
/**嵌套内部类**/
inner class Inner {
fun foo() = bar // 访问外部类成员
fun innerTest() {
//为了消除歧义,要访问来自外部作用域的 this,我们使用this@label,其中 @label 是一个 代指 this 来源的标签。
var o = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,例如:" + o.v)
}
}
}
fun main(args: Array<String>) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性
}
**区别**
嵌套类和内部类在使用时的区别
var demo = Outter.Nested()// 嵌套类,Outter后边没有括号
var demo = Outter().Inner();// 内部类,Outter后边有括号
要想构造内部类的对象,必须先构造外部类的对象,而嵌套类则不需要;
Kotlin构造函数
在Kotlin中,构造函数是一个类似于方法的代码块。 声明构造函数的名称与类的名称相同,后跟括号()。 构造函数用于在创建对象时初始化变量。
Kotlin构造函数的类型Kotlin中有两种类型的构造函数:
1.0主构造函数
2.0辅助构造函数
Kotlin类中只有一个主要构造函数,而辅助构造函数可以是一个或多个。
主构造函数
主构造函数用于初始化类,它在类标题中声明。 主构造函数代码由带有可选参数的括号括起
//声明参数name是只读属性,而id是读取和写入属性
class myClass constructor(val name: String, var id: Int) {
}
**如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略。**
class myClass (val name: String, var id: Int) {
}
fun main(args: Array<String>){
val myclass = myClass ("Susen", 10010)
println("Name = ${ myclass.name}")
println("Id = ${ myclass.id}")
}
初始化块的主构造函数
主构造函数不包含任何代码,初始化程序块用于初始化代码。 该块是以init关键字为前缀。 在实例初始化期间,初始化块的执行顺序与它们在类体中出现的顺序相同。
class myClass(name: String, id: Int) {
val e_name: String
var e_id: Int
init{
e_name = name.capitalize()
e_id = id
println("Name = ${e_name}")
println("Id = ${e_id}")
}
}
fun main(args: Array<String>){
val myclass = myClass ("Maxsu", 10010)
}
//声明name和id属性时没有val或var,因此它们不是myClass类的属性,只是作为参数传入
e_name和e_id才是类的属性
Kotlin辅助构造函数
Kotlin在类中创建一个或多个辅助构造函数。 使用constructor关键字创建辅助构造函数。
class MyClass{
var c_id:Int = 0
var c_name:String? = null
constructor(id: Int){
c_id = id
}
constructor(name: String, id: Int){
c_id = id
c_name =name
}
}
在引用的时候:
var my = MyClass(1)
var my2 = MyClass("",1)
也可以在同一个类中使用主和辅助构造函数。 通过在同一个类中使用主和辅助构造函数,辅助构造函数需要授权给主构造函数。 使用this()关键字对同一个类中的另一个构造函数进行授权。
如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:
class myClass(password: String){
constructor(name: String, id: Int, password: String): this(password){
println("Name = ${name}")
println("Id = ${id}")
println("Password = ${password}")
}
}
fun main(args: Array<String>){
val myclass = myClass ("Maxsu", 101, "passwd123")
}
从同一个类的一个辅助构造函数调用另一个辅助构造函数
在Kotlin中,一个辅助构造函数可以调用同一个类的另一个辅助构造函数。 可通过使用this()关键字完成的。
class myClass{
constructor(name: String, id: Int): this(name,id, "mypassword"){
println("第二个执行")
println("Name = ${name}")
println("Id = ${id}")
}
constructor(name: String, id: Int,pass: String){
println("第一个执行")
println("Name = ${name}")
println("Id = ${id}")
println("Password = ${pass}")
}
}
fun main(args: Array<String>){
val myclass = myClass ("Maxsu", 10010)
}
从派生类辅助构造函数调用超类辅助构造函数在Kotlin中,一个派生类的辅助构造函数可以调用基类的辅助构造函数。可使用super关键字完成的,它是继承的概念。
open class Parent{
constructor(name: String, id: Int){
println("第一个执行")
println("Name = ${name}")
println("Id = ${id}")
}
constructor(name: String, id: Int,pass: String){
println("第三个执行")
println("Name = ${name}")
println("Id = ${id}")
println("Password = ${pass}")
}
}
class Child: Parent{
constructor(name: String, id: Int): super(name,id){
println("第二个执行")
println("Name = ${name}")
println("Id = ${id}")
}
constructor(name: String, id: Int,pass: String):super(name,id,"password"){
println("第四个执行")
println("Name = ${name}")
println("Id = ${id}")
println("Password = ${pass}")
}
}
fun main(args: Array<String>){
val obj1 = Child("Susen", 10010)
val obj2 = Child("Susen", 10010,"mypassword")
}
accessModifier: 访问权限修饰符:
1.public:公开可见
2.protected:仅允许对其类或子类进行可见性
3.internal:使字段仅在实现它的模块内可见。
4.private:允许声明仅在声明属性,字段等的块内可访问,不允许访问范围外部。可以在该特定文件中访问私有包
classModifier: 类属性修饰符,标示类本身特性。
1.abstract // 抽象类
2.final // 类不可继承,默认属性
3.enum // 枚举类
4.open // 类可继承,类默认是final的
5.annotation // 注解类
类的继承
当两个或多个类具有相同的属性时,这就引入继承的概念。 继承用于提高代码可重用性。
派生类只有一个基类,但可以有多个接口,而基类可能有一个或多个派生类
(1)由于Kotlin类默认为final,因此不能简单地继承它们。在类之前使用open关键字来为其它类继承这个类。
(2)当继承一个类来派生类时,所有的字段和函数都是继承的。 可以在派生类中使用这些字段和函数。
open class Employee(name: String, age: Int, salary: Float) {
// code of employee
}
class Programmer(name: String, age: Int, salary: Float): Employee(name,age,salary) {
// code of programmer
}
class Salesman(name: String, age: Int, salary: Float): Employee(name,age,salary) {
// code of salesman
}
Kotlin继承和主要构造函数
如果基类和派生类都具有主构造函数,则参数在基类的主构造函数中初始化。
在上面的继承示例中,所有类都包含三个参数:name,age和salary,
所有这些参数都在基类的主构造函数中初始化。
当基类和派生类在主构造函数中包含不同数量的参数时,基类参数将从派生类对象初始化。
open class Employee(name: String,salary: Float) {
init {
println("姓名:$name.")
println("薪水:$salary")
}
}
class Programmer(name: String, dept: String, salary: Float):Employee(name,salary){
init {
println("$name 所在部门:$dept ,薪水为:$salary.")
}
fun doProgram() {
println("编程我有激情.")
}
}
class Salesman(name: String, dept: String, salary: Float):Employee(name,salary){
init {
println("$name 所在部门:$dept ,薪水为:$salary.")
}
fun fieldWork() {
println("我的爱好是:旅游.")
}
}
fun main(args: Array<String>){
val obj1 = Programmer("Susen", "技术部", 40000f)
obj1.doProgram()
println()
val obj2 = Salesman("Ajax", "市场部", 30000f)
obj2.fieldWork()
}
Kotlin继承和辅助构造函数
如果派生类不包含任何主构造函数,则需要使用super关键字从派生类调用基类辅助构造函数。
open class Patent {
constructor(name: String, id: Int) {
println("执行超类构造函数: $id , $name ")
}
}
class Child: Patent {
constructor(name: String, id: Int, dept: String): super(name, id) {
print("使用属性执行子类构造函数:$name, $id, $dept")
}
}
fun main(args: Array<String>) {
val child = Child("Maxsu",10010, "技术部")
}
Kotlin方法覆盖方法
覆盖意味着将super(parent)类的方法的特定实现提供到子类(子)类中。
换句话说,当子类重新定义或修改其超类的方法为子类时,它被称为方法覆盖。 方法覆盖只能在继承中实现。
Kotlin方法覆盖的规则:
1.0父类及要覆盖的方法或属性必须是open的(非final)。
2.0基类和派生类的方法名必须相同。
3.0方法必须具有与基类相同的参数。
4.0如果要覆盖父类的方法,必须将要覆盖的父类及方法声明为open。 同时,在子类中重写的方法必须以override关键字开头。
open class Bird {
open fun fly() {
println("Bird is flying...")
}
}
class Parrot: Bird() {
override fun fly() {
println("Parrot is flying...")
}
}
class Duck: Bird() {
override fun fly() {
println("Duck is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
val d = Duck()
d.fly()
}
5.0派生类也可以使用super关键字调用超类方法和属性。
open class Bird {
open var color = "黑色"
open fun fly() {
println("Bird is flying...")
}
}
class Parrot: Bird() {
override var color = "绿色"
override fun fly() {
super.fly()
println("Parrot is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
println(p.color)
}
Kotlin多类实现
在Kotlin中,派生类在尖括号中使用超类型名称,即,super ,当它实现多个类中提供的相同函数名时。
例如,派生类Parrot扩展超类Bird并实现相同的Duck接口函数fly()。 要调用每个类和接口的特定方法,必须在尖括号中提到超类型名称为super .fly()和super .fly()。(在实际开发中要尽量避免这个问题)
open class Bird {
open var color = "黑色"
open fun fly() {
println("Bird is flying...")
}
}
interface Duck {
fun fly() {
println("Duck is flying...")
}
}
class Parrot: Bird(),Duck {
override var color = "绿色"
override fun fly() {
super<Bird>.fly()
super<Duck>.fly()
println("Parrot is flying...")
}
}
fun main(args: Array<String>) {
val p = Parrot()
p.fly()
println(p.color)
}
Kotlin抽象类
1.0使用abstract关键字声明的类称为抽象类。 无法实例化抽象类。 意思是,不能创建抽象类的对象。
显式使用abstract关键声明类,才能表示抽象类的方法和属性,否则它是非抽象的。
2.0抽象类是部分定义的类,方法和属性,它们不是实现,但必须在派生类中实现。
如果派生类没有实现基类的属性或方法,那么它也是一个抽象类。
3.0抽象类或抽象函数不需要使用open关键字进行批注,因为它们默认是开放的。
抽象成员函数不包含实现主体。 如果成员函数在抽象类中包含有函数的实现,则不能将声明为abstract。
abstract class Car{
abstract fun run()
}
class Honda: Car(){
override fun run(){
println("Honda is running safely..")
}
}
fun main(args: Array<String>){
val obj = Honda()
obj.run();
}
4.0非抽象的open成员函数可以在抽象类中重载。
open class Car {
open fun run() {
println("Car is running..")
}
}
abstract class Honda : Car() {
override abstract fun run()
}
class City: Honda(){
override fun run() {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
println("Honda City is running..")
}
}
fun main(args: Array<String>){
val car = Car()
car.run()
val city = City()
city.run()
}
Kotlin接口
接口是类的蓝图(基础框架)。Kotlin接口类似于Java 8中的接口,它包含抽象方法声明以及方法的实现
接口是使用interface关键字来定义的
默认情况下,仅在没有方法体(方法只声明未实现)的情况下声明的方法是抽象的
父类或接口实现的扩展是在子类中使用(:)运算符完成的(这一点跟继承相同,不同点在于接口没有构造函数)
interface MyInterface {
val id: Int // abstract property
fun absMethod()// abstract method
fun doSomthing() {
// optional body
}
}
使用接口的优点
1.0使用接口支持多重继承功能。
2.0它可以用来实现松耦合。
3.0它用于实现抽象。
4.0一个类只能扩展一个超类但可以实现多个接口。
interface MyInterface1 {
fun doSomthing()
}
interface MyInterface2 {
fun absMethod()
}
class MyClass : MyInterface1, MyInterface2 {
override fun doSomthing() {
println("overriding doSomthing() of MyInterface1")
}
override fun absMethod() {
println("overriding absMethod() of MyInterface2")
}
}
fun main(args: Array<String>) {
val myClass = MyClass()
myClass.doSomthing()
myClass.absMethod()
}
匿名内部类
class Test {
var v = "成员属性"
fun setInterFace(test: TestInterFace) {
test.test()
}
}
/**
* 定义接口
*/
interface TestInterFace {
fun test()
}
fun main(args: Array<String>) {
var test = Test()
/**
* 采用对象表达式来创建接口对象,即匿名内部类的实例。
*/
//特别注意这里的 object : TestInterFace,这个 object 是 Kotlin 的关键字,要实现匿名内部类,
//就必须使用 object 关键字,不能随意替换其它单词,切记切记。
test.setInterFace(object : TestInterFace {
override fun test() {
println("对象表达式创建匿名内部类的实例")
}
})
}
Kotlin Data类
声明数据类必须至少包含一个带有属性参数(val或var)的主构造函数。
数据类不能是抽象的,内部的,开放的或密封的。
使用data修饰类,重写了以下函数:
equals():Boolean
hashCode(): Int
toString(): String
component() 函数对应的属性
copy()
示例:
data class User(var name: String, var id: Int, var email: String)
fun main(agrs: Array<String>) {
val u = User("Maxsu", 10010, "maxsu@yiibai.com")
println(u)
}
Kotlin Sealed类
关于封闭类看这个
密封(Sealed)类是一个限制类层次结构的类。 可以在类名之前使用sealed关键字将类声明为密封类。 它用于表示受限制的类层次结构。
当对象具有来自有限集的类型之一,但不能具有任何其他类型时,使用密封类。密封类的构造函数在默认情况下是私有的,它也不能允许声明为非私有。
密封类和 when 的使用密封类通常与表达时一起使用。 由于密封类的子类将自身类型作为一种情况。 因此,密封类中的when表达式涵盖所有情况,从而避免使用else子句。(类似于枚举)
sealed class Shape{
class Circle(var radius: Float): Shape()
class Square(var length: Int): Shape()
class Rectangle(var length: Int, var breadth: Int): Shape()
// object NotAShape : Shape()
}
fun eval(e: Shape) =
when (e) {
is Shape.Circle ->println("Circle area is ${3.14*e.radius*e.radius}")
is Shape.Square ->println("Square area is ${e.length*e.length}")
is Shape.Rectangle ->println("Rectagle area is ${e.length*e.breadth}")
//else -> "else case is not require as all case is covered above"
// Shape.NotAShape ->Double.NaN
}
fun main(args: Array<String>) {
var circle = Shape.Circle(5.0f)
var square = Shape.Square(5)
var rectangle = Shape.Rectangle(4,5)
eval(circle)
eval(square)
eval(rectangle)
}
从Kotlin调用Java代码
如果想要从Kotlin文件调用存在于不同包中的Java代码,则需要在Kotlin文件中使用Java类导入包名
//无返回值
fun main(args: Array<String>) {
val sum= MyJavaClass.add(5, 10)
println("printing sum inside Kotlin file: "+sum)
}
public class MyJavaClass {
public static void main(String[] args){
}
public static void add(int a,int b){
int result = a + b;
System.out.println("printing inside Java class :"+result);
}
}
//有返回值
fun main(args: Array<String>) {
val area: Int = MyJavaClass.area(3, 4)
println("printing area from java insideKotlin file: "+area)
}
public class MyJavaClass {
public static void main(String[] args){
}
public static int area(int a, int b){
int result = a * b;
return result;
}
}
从Java调用Kotlin代码
//kotlin代码
fun main(args: Array<String>){
//code
}
fun area(a: Int, b: Int):Int{
return a*b
}
//java代码
public class MyJava {
public static void main(String[] args) {
int area = MyKotlinKt.area(4,5);
System.out.print("printing area inside Java class returning from Kotlin file: "+area);
}
}
如果想要从Java类调用不同包中的Kotlin代码,则需要在Java类中导入包含Kotlin文件名的包名,
并从Java类调用Kotlin代码。 另一种方法是指定完整路径:packageName.KotlinFileKt.methodName()来调用
使用注解**@JvmName**更改Kotlin文件名
编写Kotlin代码并在顶部放置注解@file:JvmName(“MyKotlinFileName”)。 编译Kotlin代码后,文件名将更改为注释中提供的名称(如:MyKotlinFileName)。 在访问MyKotlin.kt的代码时,需要使用文件名:MyKotlinFileName。
//kotlin代码
@file: JvmName("MyKotlinFileName")
package mykotlinpackage
fun main(args: Array<String>) {
}
fun area(l: Int,b: Int):Int{
return l*b
}
//java引用
package myjavapackage;
import mykotlinpackage.MyKotlinFileName;
public class MyJavaClass {
public static void main(String[] args){
int area = MyKotlinFileName.area(4,5);
System.out.println("printing area inside Java class returning from Kotlin file: "+area);
}
}
使用**@JvmMultifileClass**调用具有相同Java类名的多个文件方法
如果Kotlin的多个文件使用@JvmName注解生成相同的Java文件名,则通常在从Java文件调用时会出错。 但是,Kotlin编译器生成单个Java façade类,其中包含生成的Java文件以及具有相同名称的文件的所有声明。 为了激活façade类,我们在所有文件中使用@JvmMultifileClass注解。
文件:MyKotlin1.kt
@file: JvmName("MyKotlinFileName")
@file:JvmMultifileClass
package mykotlinpackage
fun main(args: Array<String>) {
}
fun area(l: Int,b: Int):Int{
return l*b
}
文件:MyKotlin2.kt
@file: JvmName("MyKotlinFileName")
@file:JvmMultifileClass
package mykotlinpackage
fun volume(l: Int,b: Int,h: Int):Int{
return l*b*h
}
文件:MyJava.java
package myjavapackage;
import mykotlinpackage.MyKotlinFileName;
public class MyJavaClass {
public static void main(String[] args){
int area = MyKotlinFileName.area(4,5);
System.out.println("printing area inside Java class returning from Kotlin file: "+area);
int vol = MyKotlinFileName.volume(4,5,6);
System.out.println("printing volume inside Java class returning from Kotlin file: "+vol);
}
}