运算符重载
kotlin中每一个运算符都是对应的一个方法,运算符相当于方法的简写
operator,找到对应的函数,在函数前面加上operator
class demo5 {
//运算符重载,重载+方法
operator fun plus(num: Int): Int {
var i = num
var sum = 0
while (i > 0) {
sum += i
i--
}
return sum
}
//运算符重载,重载-方法
operator fun minus(num: Int): Int {
var i = num
var sum = 0
while (i > 0) {
sum += i
i--
}
return sum
}
operator fun rangeTo(num: Int):Int{
var i = num
var sum = 0
while (i > 0) {
sum += i
i--
}
return sum
}
}
fun main() {
val demo5 = demo5()
println(demo5 - 10)//相当于调用demo5.minus(10)方法
println(demo5..100)
}
- 底层java代码
get和set
- kotlin中的字段是私有的,会自动生成get/set方法,如果是var修饰,则会生成get/set,如果是val修饰,则会生成get
- 如何让外部代码禁止修改字段?,不能使用val,如果使用val,内部也无法修改
var age: Int = 10
private set
- 在set中添加逻辑
var age: Int = 10
set(value) {//value是外部传递进来
//field相当于age的值
//不能直接修改age属性:age = value //error,相当于死递归了
//field = value
if (value == 100) {
field = 10
} else if (value == 101) {
field = 20
} else {
field = -1
}
}
面向对象
构造函数
主构函数
一个类中最多只能一个主构函数,也可以没有,在类名后面
class Person(name: String, age: Int) {}
//或者
class Person constructor(name:String,age:Int){}
主构造器声明属性
- 有参构造函数,属性只能在构造函数中使用
class Person(var name: String, val age: Int) {
}
//或者
class Person(name:String){
var name:String = name
}
次构函数
相当于java中的构造函数重载
次构函数中的字段不能使用var/val,只能在外部定义字段,然后字段赋值
当定义次构函数时,必须调用主构函数(直接或间接)
无论调用主构函数还是次构函数都会执行init
var phone:String=""
constructor(name: String, age: Int, phone: String) : this(name, age) { //this(),也是相当于java中调用其他的构造函数
this.phone = phone
}
//错误
constructor(name: String, age: Int, phone: String){}
主构造器,init和次构函数的执行顺序
主构造器优先执行,接着执行init代码块,最后执行次构造函数。
主构造器相当于头部,init相当于颈部,次构造器相当于身体
封装
隐藏功能实现的细节,暴露外部所需要功能的接口
继承
kotlin中的class都是final,不能被继承
需要在类前面添加open关键字,该类才能被继承
使用 class C : P(参数列表)
属性继承也是一样,在java中属性的get/set也是final修饰,也需要open
方法也是需要使用open,才能被继承
fun main() {
val p: People = Student()
println(p.name)
println(p.age)
p.say()
}
open class People(open var phone: String) {
open var name: String = ""//默认是final的,需要添加open关键字,去掉final
var age = 0
open fun say() {
}
}
class Student : People("888") {
//相当于重新了get/set
override var name: String = "张三"//属性前面需要添加override关键字
override var phone: String = "11223365"
override fun say() {
println("student say")
}
}
抽象类
抽象类中不需要open关键字
和java一样,抽象类中可以没有抽象方法
和java一样,也是单继承
抽象类也是可以继承抽象类
fun main() {
val man :Human = ZhMan()
man.say()
}
abstract class Human {
//底层是set/get方法抽象
protected abstract var color: String
abstract var lang: String
abstract fun say()
}
class ZhMan : Human() {
//重新了父类的方法
override var color: String = "黄色"
override var lang: String = "中文"
override fun say() {
println("我是zhman:${color},${lang}")
}
}
接口
接口实现也是使用冒号 class C : P
接口可以多实现
kotlin中接口中的字段是不能实现的,底层是set/get方法。java中接口字段必须实现,而且是static final的,相当于常量
interface ICallback {
//接口中的字段不能实现,其实底层也是set/get
var i: Int
}
//实现接口
class demo1 : ICallback {
override var i : Int
get() {
return 10
}
set(value) {}
}
public interface ICallback {
int getI();
void setI(int var1);
}
如果在接口定义常量,需要使用伴生对象,底层java代码是,在接口中定义了一个静态内部类
interface ICallback {
companion object {
const val NAME = ""
fun say() {}
}
//接口中的字段不能实现,其实底层也是set/get
var i: Int
}
kotlin接口中方法时可以默认实现的,底层java其实是在接口中定义了一个静态内部类实现了此接口,实现了该方法,当外部实现此接口时,会在该方法中,创建该静态内部类,从而调用了该抽象方法。
interface ICallback {
//接口中方法可以实现
fun hello() {
println("hello")
}
}
public interface ICallback {
void hello();
public static final class DefaultImpls {
public static void hello(@NotNull ICallback $this) {
String var1 = "hello";
boolean var2 = false;
System.out.println(var1);
}
}
}
//调用时
public static final void main() {
(new ICallback() {
private int i = 10;
public int getI() {
return this.i;
}
public void setI(int var1) {
this.i = var1;
}
public void hello() {
ICallback.DefaultImpls.hello(this);
}
}).hello();
}
智能类型转换
使用is关键字,判断类型是否相同
使用as关键字,进行类型强转
如果使用is判断了,则会智能推断出当前对象为指定的类型,不需要强转
//强转
val newMan = man as ZhMan
val man :Human = ZhMan()
if (man is ZhMan){//智能转换
man.say()
}
内部类
局部内部类
可以定义在方法中,与java一致
fun main() {
class Inner{
}
}
成员内部类和静态内部类
class Out{
//静态内部类
class StaticInner{
}
//成员内部类
inner class Inner{
//使用外部属性和方法时,使用this@外部类名
this@Out.xxx
}
}
匿名内部类
//匿名内部类
val obj = object : Inner() {}
泛型
泛型类
和java一致,泛型定义在类上,用<>表示
成员属性和成员方法也可以使用类泛型
静态属性和静态方法就不能使用类泛型。类一加载时,静态属性和静态方法就加载了,这时无法确定类的泛型类型
与java的泛型一致
fun main() {
val demo8 = Demo8<String>()
demo8.say("张三")
}
class Demo8<U> {
var name: U? = null
companion object {
//静态属性和静态方法无法使用类泛型
//和java一样
//类一加载时,静态属性和静态方法就加载了,这时无法确定类的泛型类型
var n: U? = null//错误,编译不通过
fun staticSay(u: U) {//错误,编译不通过
}
//定义泛型方法
fun <J> staticSay2(j: J) {
println(j)
}
}
//方法使用类泛型
fun say(u: U) {
println(u)
}
}
泛型方法
和java的泛型方法一致
//定义泛型方法
fun <J> staticSay2(j: J) {
println(j)
}
泛型限定
泛型定义,该类型或该类型的子类
class FruitBox< f : Fruit> 只能存放Fruit或Fruit的子类
和java一样,在类上定义泛型时,只能是该类型或者该类型的子类,不能定义该类型的父类
- java
class Person<E extends Number>{}
//没有接收类型的父类
class Person<E super Number>{}//错误
泛型插除
参数化类型的泛型参数只在编译期间有效,在运行期间就被插除了
获取泛型类型
//获取类上的泛型类型
fun haha() {
val cls = thing!!::class.java
println(cls)
}
//获取方法的泛型类型
fun <E> haha2(name: E) {
val cls = name!!::class.java
println(cls)
}
inline reified 获取泛型类型
inline fun <reified E> haha3(name: E) {
val cls = E::class.java//这里只是渐变的写法,直接使用泛型获取类型,不需要使用对象获取
println(cls)
}
泛型类型投射
泛型通配符,这里是使用泛型,注意和类泛型的声明(定义)区别
泛型的上限Out
接收当前类型或它的子类,相当于java中的<? extends Type>
fun f1(list: ArrayList<out Fr>) {
}
static void say(List<? extends Number> list){
}
泛型的下限in
fun f1(list: ArrayList<in Fr>) {
}
static void say(List<? super Number> list){
}
泛型星号投射
*相当于java中的?,可以传递任意类型
fun f1(list: ArrayList<*>) {
}
static void say(List<?> list) {
}