Kotlin入门学习记录
2023/12/03 - 2023/12/04
方法重写
在KT中默认所有属性和方法以及类都为final
修饰,即不可继承,如果想要能够被继承需要使用open
关键字修饰,并且在重写时需要在方法前加上override
关键字,代码实现如下
open class Product(val name:String){
fun description() = "Product:$name"
open fun load() = "Nothing"
}
class XiaomiProduct:Product("Xiaomi 14"){
override fun load(): String = "XiaomiProduct loading"
}
fun main() {
val p:Product = XiaomiProduct()
println(p.load())//XiaomiProduct loading
}
类型识别
使用is
关键字和Java中的instanceof
关键字类似,代码实现如下
...
fun main() {
val p:Product = XiaomiProduct()
println(p is XiaomiProduct)//true
println(p is Product)//true
println(p is File)//false
}
...
强制类型转换
KT中使用as
关键字进行强制类型转换,调用子类方法,代码实现如下
class XiaomiProduct:Product("Xiaomi 14"){
...
fun special() = "XiaomiProduct special fun"
}
fun main() {
val p:Product = XiaomiProduct()
if (p is XiaomiProduct) {
println((p as XiaomiProduct).special())
//XiaomiProduct special fun
}
}
Java实现代码如下
public static void main(String[] args) {
Product p = new XiaomiProduct();
if (p instanceof XiaomiProduct){
System.out.println(((XiaomiProduct) p).special());
//XiaomiProduct special fun
}
}
智能类型转换
当需要频繁调用子类方法时,在Java中的多态需要频繁转换类型,在KT中只需要转换一次就可以调用子类方法,代码实现如下
fun main() {
val p:Product = XiaomiProduct()
println((p as XiaomiProduct).special())//XiaomiProduct special fun
println(p.special())//XiaomiProduct special fun
}
将Jvm字节码转为Java,如下,
从var1 = ((XiaomiProduct)p).special();
可以看出编译器自动将类型强制转换
public static final void main() {
Product p = (Product)(new XiaomiProduct());
String var1 = ((XiaomiProduct)p).special();
System.out.println(var1);
var1 = p.load();
System.out.println(var1);
var1 = ((XiaomiProduct)p).special();
System.out.println(var1);
}
Any超类
在KT中所有的类都继承Any
就像Java中的Object
一样,KT中的Any
包含的函数如下
注意
Any类中的函数没有具体实现,是因为KT要实现跨平台性,所以放到编译器中去实现函数
Any类中的equals
(==
)比较的是引用和String
中的===
一样
public open class Any {
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
}
对象
使用object
关键字创建单例类,object
关键字有三种方式:对象声明(object declaration)、对象表达式(object expression)和伴生对象(companion object)
对象声明
object ApplicationConfig{
init {
println("loading config")
}
fun setSomething(){
println("setSomething")
}
}
fun main() {
ApplicationConfig.setSomething()
}
对象表达式
KT中的对象表达式和Java中的匿名内部类类似,不同的是Java中只能是实现一个接口或类,但KT可以实现多个,代码实现如下
open class Player{
open fun load() = "load nothing"
}
fun main() {
val p = object : Player(){
override fun load(): String =
"anyone classload"
}
println(p.load())//anyone classload
}
伴生对象
如果你想将某个对象的初始化和一个类实例捆绑在一起,可以考虑使用伴生对象。使用
companion
,代码实现如下
注意
一个类只能有一个伴生对象,包含伴生对象的类初始化时,伴生对象就会被初始
化
open class ConfigMap{
companion object{
private const val PATH = "test.txt"
fun load() = File(PATH).readLines()
}
}
fun main() {
println(ConfigMap.load())
}
嵌套类
就是类中类,相当于Java中的内部类,默认为static
修饰,代码实现如下
class Player2{
class Equipment(var name:String){
fun show() = println("equipment $name")
}
fun battle(){
Equipment("LY_C").show()
}
}
fun main() {
Player2().battle()
Player2.Equipment("CRY").show()
}
Java代码实现如下
public class Player2 {
static class Equipment{
String name;
public Equipment(String name) {
this.name = name;
}
void show(){
System.out.println("equipment"+name);
}
}
void battle(){
new Equipment("LY_C").show();
}
}
class MAIN{
public static void main(String[] args) {
new Player2().battle();
new Player2.Equipment("LY_C").show();
}
}
数据类
数据类就是使用了data
关键字的class
,重写了超类的toString
、equals
方法,添加了方便复制对象的copy
方法
toString
重写了toString
方法,代码实现如下
data class CardinalDirection(var x:Int,var y:Int) {
val isInBounds = x > 0 && y > 0
}
fun main() {
println(CardinalDirection(10, 20))
//CardinalDirection(x=10, y=20)
}
equals
重写了Any的equals
(==
)方法,从比较引用改成比较内容,代码实现如下
...
fun main() {
...
println(CardinalDirection(10, 10) == CardinalDirection(10, 10))
//true
}
copy
类似于Java中的clone
,用来方便地复制一个对象,代码实现如下
data class Student(var name:String,var age:Int){
var scorce = 10
private val habby = "music"
var subject:String
init {
subject = "Kotlin编程权威指南"
}
constructor(_name:String):this(_name,21){
scorce = 90
}
override fun toString(): String {
return "Studnet $name $age " +
"scorce $scorce,habby $habby" +
"subject $subject"
}
}
fun main() {
val student = Student("LY_C")
println(student)
//Studnet LY_C 21 scorce 90 ,habby music subject Kotlin编程权威指南
val copy = student.copy("CRY")
println(copy)
//Studnet CRY 21 scorce 10 ,habby music subject Kotlin编程权威指南
}
结构声明
data
类中自动添加解构声明,后台实现就是声明 component1、component2 等若干个组件函数,让每个函数负责管理你想返回的一个属性数据,Jvm中转为Java代码实现如下
public final class Student {
...
@NotNull
public final String component1() {
return this.name;
}
public final int component2() {
return this.age;
}
...
}
解构代码使用如下
fun main(){
...
var (name,age) = Student("LY_C",21)
println(name + age)//LY_C21
...
}
将上方代码从Jvm中转为Java,代码如下
...
public static final void main() {
Student var2 = new Student("LY_C", 21);
String name = var2.component1();
int age = var2.component2();
String var3 = name + age;
System.out.println(var3);
}
...
普通Class中的结构声明
利用data
类解构声明的原理,使用operator
关键字重写componentx
方法,代码实现如下
class Player(val x:Int,val y:Int){
operator fun component1() = x
operator fun component2() = y
}
fun main() {
val (x,y) = Player(10,20)
println(x + y) //30
}
注意
数据类必须有至少带一个参数的主构造函数
数据类主构造函数的参数必须是 val 或 var
数据类不能使用 abstract、open、sealed 和 inner 修饰符
重载运算符
要将内置运算符应用在自定义类身上,必须重写运算符函数,例如使用plus
和其关联的+
运算符实现属性值的相加时,使用operator
关键字重写plus
方法,代码实现如下
data class Coordindate2(var x:Int,var y:Int){
val isInBounds = x > 0 && y > 0
operator fun plus(other:Coordindate2) =
Coordindate2(x + other.x,y + other.y)
}
fun main() {
val c1 = Coordindate2(10,20)
val c2 = Coordindate2(10,29)
println(c1 + c2)//Coordindate2(x=20, y=49)
}
常见操作符如下
操作符 | 函数名 | 作用 |
---|---|---|
+ | plus | 把一个对象添加到另一个对象里 |
+= | plusAssign | 把一个对象添加到另一个对象里,然后将结果赋值给第一个对象 |
== | equals | 如果两个对象相等,则返回 true,否则返回 false |
> | compareTo | 如果左边的对象大于右边的对象,则返回 true,否则返回 false |
[] | get | 返回集合中指定位置的元素 |
.. | rangeTo | 创建一个 range 对象 |
in | contains | 如果对象包含在集合里,则返回 true |
枚举类
- 使用
enum
关键字声明枚举类,除了Java中的常用方法以外,还可以添加一个主构造函数,每个枚举常量也要传递参数,代码实现如下
enum class Direction {
EAST,
WEST,
SOUTH,
NORTH
}
...
fun main() {
println(Direction.EAST)//EAST
}
Java代码实现如下
enum Direction {
EAST,
WEST,
SOUTH,
NORTH
}
public class Direction1{
public static void main(String[] args) {
System.out.println(Direction.EAST);//EAST
}
}
- 添加构造函数,下方代码由于传递的是Coordinate类,并且每一个枚举项本质上都是枚举类自身的实例,所以枚举中的实例都要传递Coordinate对象,代码实现如下
enum class Direction(private val coordinate:Coordinate) {
EAST(Coordinate(5,-1)),
WEST(Coordinate(1,0)),
SOUTH(Coordinate(0,1)),
NORTH(Coordinate(-1,0));
fun updateCoordindate(playerinate:Coordinate) =
Coordinate(playerinate.x + coordinate.x,
playerinate.y + coordinate.y)
}
data class Coordinate(val x: Int, val y: Int) {
val isInBounds = x >= 0 && y >= 0
}
fun main() {
println(Direction.EAST.updateCoordindate(Coordinate(100, 200)))
//Coordinate(x=105, y=199)
}
代数数据类型
代数数据类型用来表示一组子类型的闭集,枚举类型相当于简单的ADT
(代数数据类型
),因为每个枚举常量都为枚举本身的实例,使用枚举类型和其他代数数据类型有一个好处,即编译器会帮你检查代码处理是否有遗漏,因为代数数据类型表示的是一组子类型的闭集,代码实现如下
enum
实现如下
enum class LicenseStats{
UNQUALIFIED,
LEARNING,
QUALIFIED
}
class Driver(var stats: LicenseStats){
fun checkLicense():String {
return when(stats){
LicenseStats.QUALIFIED -> "合格"
LicenseStats.LEARNING -> "在学"
LicenseStats.UNQUALIFIED -> "不合格"
}
}
}
fun main() {
println(Driver(LicenseStats.LEARNING.checkLicense()))//在学
}
编译器检查代码处理有遗漏,如有遗漏直接爆红
密封类
对于更复杂的 ADT,你可以使用 Kotlin 的密封类(sealed class
)来实现更复杂的定义。密封类可以用来定义一个类似于枚举类的 ADT,但你可以更灵活地控制某个子类型。密封类可以有若干个子类,子类都要继承密封类,这些子类必须和它定义在同一个文件里,代码实现如下
sealed class LicenseStats2{
object Unqualified :LicenseStats2()
object Learning :LicenseStats2()
class Qualified(val licenseId:String):LicenseStats2()
}
class Dricer2(var stats:LicenseStats2){
fun check():String{
return when(stats){
is LicenseStats2.Learning -> "在学"
is LicenseStats2.Qualified -> "合格 编号为 " +
"${(stats as LicenseStats2.Qualified).licenseId}"
is LicenseStats2.Unqualified -> "不合格"
else -> {
"Error"
}
}
}
}
fun main() {
println(Dricer2(LicenseStats2.Qualified("3114541245")).check())
//合格 编号为3114541245
}