做网站需要软件,网站开发要学些什么,深圳宝安做网站的公司,南宁哪里有网站建设培训班Kotlin程序设计#xff08;扩展一#xff09;
**注意#xff1a;**开启本视频学习前#xff0c;需要先完成以下内容的学习#xff1a;
请先完成《Kotlin程序设计》视频教程。请先完成《JavaSE》视频教程。
Kotlin在设计时考虑到了与Java的互操作性#xff0c;现有的Ja…
Kotlin程序设计扩展一
**注意**开启本视频学习前需要先完成以下内容的学习
请先完成《Kotlin程序设计》视频教程。请先完成《JavaSE》视频教程。
Kotlin在设计时考虑到了与Java的互操作性现有的Java代码可以自然地调用Kotlin代码而Kotlin代码也可以轻松兼容Java的调用。在本扩展篇中我们会讲解如何通过Kotlin调用Java代码。
Kotlin调用Java
我们先从最基本的内容说起现在需要让Kotlin与Java互相兼容并不是直接就可以使用的我们还要遵循某些约定才可以使得Java兼容Kotlin的语法。
类的定义和使用
在Java中最关键的就是类我们来看看如何在Kotlin中进行使用。
我们在Java中定义的类型可以非常轻松地被Kotlin使用比如下面这个由Java语言定义的类型
public class Student {int age;String name;
}在Kotlin中我们可以直接使用这个类就像是在Kotlin中定义的那样
fun main() {val student: Student Student() //直接使用Java中的类型无缝衔接student.name 小明println(student.name) //这里得到的Java中的String类型可以直接当做Kotlin中的使用
}以及Kotlin中我们提到的一些基本类型都可以与Java中的基本类型互相转换
fun main() {val student Student()val age: Int student.age //Java中的int/Integer对应了Kotlin中的Int
}几乎Java中所有基本类型在Kotlin中都存在对应的类型所以说直接转换为Kotlin支持的基本类型也是可以的。
包括我们在类中定义的方法也可以在Kotlin中被当做函数使用
public class Student {String name;public void hello() {System.out.println(大家好我叫 name);}
}fun main() {val student Student()student.name 小明student.hello() //函数调用
}注意如果方法的返回类型是void那么它对应的就是Kotlin中的Unit类型。
包括在Java中定义的构造方法也可以被Kotlin当做构造函数使用因为它们的语法其实差不多可以很轻松完成兼容
public class Student {String name;public Student(String name) {this.name name;}
}fun main() {val student Student(小明)
}可以看到这些内容几乎是没有多少学习成本的包括在Java类中定义的静态内容
public class Student {public static void test() {System.out.println(我是测试静态函数);}
}这些静态属性就像使用Kotlin中的伴生对象一样可以直接通过类名进行调用这跟Java中是一样的
fun main() {Student.test()
}还有由于Kotlin与Java中的关键字存在差异我们在Java中定义的某些属性名称可能会成为Kt的关键字
import java.io.InputStream;
public class Student {InputStream in; //Java中没有问题因为in不是关键字
}fun main() {val student Student()//在Kotlin中由于in是关键字因此我们需要对其进行转义来消除冲突//使用字符来完成转义student.in FileInputStream(C://)
}包括Java中的可变参数也是可以直接兼容的
public class Student {public void test(String... args) {}
}我们也可以直接继承Java中提供的类型
public class Student {}class ArtStudent: Student() //语法与之前是一样的在后续的学习中我们再来继续认识更多高级的内容。
Getter和Setter
在Kotlin基础教程中我们说到类中的成员属性可以具有自己的Getter和Setter函数比如
class Student {var name: String get() fieldset(value) {field value}
}这样我们就可以实现对于这个变量赋值和获取的进一步控制比如我们希望在赋值时打印内容
var name: String get() fieldset(value){println(我被赋值了) //由于get和set本质上编译后是函数因此可以自定义field value}而我们知道在Java中一个类的属性并不能像这样去编写
public class Student {String name; //只能定义一个变量非常简单
}我们可以对Java中的这个属性进行封装使得其支持像Kotlin中那样存在Getter和Setter函数
public class Student {private String name; //将name属性private掉public String getName() { //自定义Get和Set方法设置name属性return name;}public void setName(String name) {this.name name;}
}fun main() {val student Student()student.name 小明println(student.name)
}这样编写之后我们同样可以在Kotlin中直接使用对应属性的名称进行访问但是这本质上是通过其Get和Set函数来完成的在获取属性时会调用getName()方法得到对应的结果设置同理。 注意这个Get和Set必须遵循命名规则比如这里我们要为name属性添加Getter方法那么必须要命名为get Name这样的名称来表示对name属性的命名必须以get开头而Setter必须以set开头。 如果返回值类型是一个Boolean类型那么Getter方法名称需要以is开头而Setter同上。 注意由于此时name属性由于存在访问权限控制无法被外部访问如果我们去掉Setter或是Getter函数将导致变量只能被赋值或是不可用比如去掉set方法后 注意 如果直接去掉Getter方法无论是否保留Setter方法都会导致这个变量不可用因为Kotlin不支持仅set-only属性。
空安全处理
由于在Java中的任何引用类型值都可能是null这使得Kotlin对来自Java的对象进行空安全检测不太方便。因此对于Java声明的类型会在Kotlin中以特定方式处理我们称为平台类型。对于这种类型空检查是放宽的因此它们的安全保证与Java相同也就是说部分情况下不会进行空安全检查。
比如下面这个例子
public class Student {String name; //默认情况下name属性的值就是null
}fun main() {val student Student()//此时name在Kotlin中为平台类型不会进行空安全检查这里可以编译通过println(student.name.uppercase())
}很明显上面的代码出现了空指针异常因为这里没有进行任何的空安全检查。
对于这种平台类型IDEA会给我们明确指出比如这里的name属性时String!类型的它表示这个类型可能是Srting或String?的其中一种 我们在接受这个属性的时候由于其特殊性也可以使用两种
fun main() {val student Student()val name: String student.name //直接使用不可空类型接受但是可能会出错val name2: String? student.name //直接使用可空类型接受test(student.name) //函数同样适用此规则
}fun test(str: String) { }如果我们使用了一个不可空类型接受到来自Java的null值会直接得到一个空指针异常这可以防止Kotlin的不可空变量持有空值包括传递函数参数时也同样适用总的来说编译器在尽最大努力防止空值在Kotlin程序中传播。
注意在某些情况下持有可空注解的Java类型不会表现为平台类型比如
public class Student {NotNull String name; //由JetBrains提供的注解
}这些注解包括
JetBrains来自org.jetbrains.annotations包下的Nullable和NotNullJSpecifyorg.jspecify.nullnessAndroidcom.android.annotations和android.support.annotationsJSR-305javax.annotationFindBugsedu.umd.cs.findbugs.annotationsEclipseorg.eclipse.jdt.annotationLomboklombok.NonNullRxJava 3io.reactivex.rxjava3.annotations
同样的对于一些泛型类也存在一些空类型检查问题这里以List为例
public class Student {ListString exams;
}fun main() {val student Student()val exams1: MutableListString?? student.exams //支持多种方式val exams2: MutableListString? student.examsval exams3: ListString?? student.exams...
}可以看到在我们使用Java中提供的List时会得到 这里的(Mutable)ListString!!包含了很多信息我们依次来解读一下
首先Mutable表示这个List可以是可变的也可以是不可变的因为在Java中并没有明确划分可变或是不可变的数组。然后这里的类型参数String和List都带有!表示他们都可以是可空类型也可以是不可空类型。
我们同样可以使用非空注解来提醒编译器这里一定不会为null防止被认定为平台类型
public class Student {NotNull ListString exams;
}以及在Java中的数组类型对应的就是Kotlin中的Array类型
public class Student {String[] exams;
}由于数组在Java支持协变这与Kotlin存在不同因此这里我们使用Java中的数组时可以将其当做一个抗变或是协变的String类型进行使用
fun main() {val student Student()val exams1: ArrayString student.examsval exams2: Arrayout String? student.examsval exams3: Arrayout String?? student.exams
}这里的到的Array可以是可空也可以是不可空里面的类型参数String同样可以是可空或是不可空并且可以是协变也可以是抗变的。
类型对照表
前面我们提到在Kotlin中存在Java中相应的基本类型我们在使用Java提供的类型时可以直接转换使用
Java类型Java包装类型Kotlin类型bytejava.lang.Bytekotlin.Byteshortjava.lang.Shortkotlin.Shortintjava.lang.Integerkotlin.Intlongjava.lang.Longkotlin.Longcharjava.lang.Characterkotlin.Charfloatjava.lang.Floatkotlin.Floatdoublejava.lang.Doublekotlin.Doublebooleanjava.lang.Booleankotlin.Boolean
注意虽然Java类型可以映射到Kotlin对应的类型但是平台类型性质依然保留
public class Student {Integer age;
}可以看到对于Java中的包装类型Integer这里虽然可以直接转换为Int类型但是它依然可以是Int?或是Int这两种类型。只不过对于Java中的基本类型来说由于不存在null这种结果因此我们可以安全的将其当做不可空类型使用
public class Student {int age;
}除了这些基本类型之外实际上Kotlin中还有很多其他类型也可以直接映射
Java类型Kotlin类型java.lang.Objectkotlin.Any!java.lang.Cloneablekotlin.Cloneable!java.lang.Comparablekotlin.Comparable!java.lang.Enumkotlin.Enum!java.lang.annotation.Annotationkotlin.Annotation!java.lang.CharSequencekotlin.CharSequence!java.lang.Stringkotlin.String!java.lang.Numberkotlin.Number!java.lang.Throwablekotlin.Throwable!
集合类型在Kotlin中可以是只读的或可变的因此Java的集合映射如下此表中的所有Kotlin类型都定义在kotlin.collections包中
Java类型Kotlin只读类型Kotlin可变类型转换平台类型IteratorTIteratorTMutableIteratorT(Mutable)IteratorT!IterableTIterableTMutableIterableT(Mutable)IterableT!CollectionTCollectionTMutableCollectionT(Mutable)CollectionT!SetTSetTMutableSetT(Mutable)SetT!ListTListTMutableListT(Mutable)ListT!ListIteratorTListIteratorTMutableListIteratorT(Mutable)ListIteratorT!MapK, VMapK, VMutableMapK, V(Mutable)MapK, V!Map.EntryK, VMap.EntryK, VMutableMap.MutableEntryK,V(Mutable)Map.(Mutable)EntryK, V!
以及数组的映射如下基本类型的数组会被直接映射为基本类型专用的Array类型
Java类型Kotlin类型int[]kotlin.IntArray!String[]kotlin.Array(out) String!
注意在Java中这些类型可能存在一些静态属性如果我们需要调用对应的静态属性需要使用Java中类型的名称进行调用
//在Integer中定义的静态方法
public static int parseInt(String s) throws NumberFormatException {return parseInt(s,10);
}fun main() {Integer.parseInt(666) //需要使用原本的名称而不是转换之后的Int
}泛型转换
Kotlin的泛型与Java型有点不同当将Java类型导入Kotlin时将完成以下转换
Java的通配符转换 Foo? extends Bar成为Fooout Bar!!Foo? super Bar成为Fooin Bar!!
public class Student {List? extends Number data; //此时泛型上界为NumberList? super Integer data2; //此时泛型下界为Integer
}当这些类型在Kotlin中使用时会自动被划分为协变或抗变类型
fun main() {val student Student()//Java中的泛型上界对应Kotlin的协变类型val data: MutableListout Number student.data//Java中的泛型下界对应Kotlin的抗变类型val data2: MutableListin Int student.data2
}由于在Kotlin中对in和out进行了严格的使用限制因此无法像Java那样随意使用。
Java的原始类型被转换为星形投影 List当做List*!也就是Listout Any?!
public class Student {List data; //对List的原始使用
}fun main() {val student Student()//在Kotlin中直接以Any?作为实际类型使用因为没有明确具体类型val data: MutableListAny?? student.data
}与Java一样Kotlin的泛型不会在运行时保留也就是说对象不会带有任何实际类型参数的信息详情请见基础篇。
运算符重载
由于Java不支持运算符重载我们无法通过关键字支持像Kotlin这样的运算符重载但是只要符合我们前面所说的那些运算符重载函数名称的方法依然可以作为运算符重载函数使用
public class Student {public Student plus(Student other) {return this;}
}可以看到以上代码与Kotlin中运算符重载函数的定义相同满足规范因此在Kotlin中可以直接支持使用
fun main() {var student Student()student student student
}异常检查
在Kotlin中所有异常都不会主动进行检查这意味着编译器不会强迫您捕获任何异常。因此当您调用声明了异常的Java方法时Kotlin不会强制要求进行捕获
public class Student {public void test() throws IOException {}
}fun main() {var student Student()student.test()
}Object类型
当Java对象在Kotlin中使用时Object类型的所有引用都会变成Any类型。由于Any类型不是特定于某一个平台的考虑到对其他语言的兼容性因此它只声明toString()hashCode()和equals()函数作为其成员而在Java中Object存在很多其他的成员方法
public final native Class? getClass();
public final native void notify();
public final native void notifyAll();
...如果需要让Object类中的其他成员方法可用可以像下面这样
fun main() {val student Student()//将student类型转换为Object再调用其(student as Object).wait()
}不过在Kotlin中不鼓励使用wait()和notify()等线程相关方法使用JUC中提供的类型效果更佳详情请见Java JUC篇视频教程
如果我们要获取某个类的Class对象
fun main() {val student Student()val clazz: ClassStudent student.javaClass //使用.javaClass获取到对应的Java类对象
}函数式接口
Kotlin支持Java的SAM转换只要是Java中满足要求的函数式接口都可以开箱即用
FunctionalInterface
public interface Runnable {public abstract void run();
}fun main() {//使用Java中的Runnable接口val runnable: Runnable Runnable {println(Hello World)}
}包括我们在函数中一样可以像这样使用
import java.util.concurrent.ThreadPoolExecutorfun main() {val executor ThreadPoolExecutor()// Java方法定义: void execute(Runnable command)executor.execute { println(This runs in a thread pool) }
}Java调用Kotlin
前面我们介绍了Kotlin如何调用现成的Java代码我们接着来看Java如何调用Kotlin代码。由于Java和Kotlin之间存在某些差异在将Kotlin代码集成到Java中时需要注意很多东西。
对象属性
在Kotlin中定义的类型到了Java中依然可以直接创建其对象
class Student(var name: String)public static void main(String[] args) {Student student new Student(小明); //Kt构造函数就是构造方法
}只不过对于类的属性由于Kotlin中本质是以get和set函数的形式存在的因此我们只能使用对应的Getter和Setter方法来进行调用
public static void main(String[] args) {Student student new Student(小明);student.getName();student.setName(大明); //set方法仅在属性为var时可用
}如果实在是需要在Java中像使用普通变量那样我们可以添加一个特殊的注解使其支持
class Student(JvmField var name: String)包括懒加载属性也支持
class Student {lateinit var name: String
}public static void main(String[] args) {Student student new Student(小明);student.name ;
}当然这个属性不得是 open、override或const 其中一种也不能是委托属性。
同时由于Java中不存在空类型处理因此在Kotlin中定义的无论是否为可空类型都可以在Java中直接使用
class Student {var name: String? null
}public static void main(String[] args) {Student student new Student();student.getName().toUpperCase(); //直接空指针异常
}静态属性
如果是Kotlin文件中直接编写的顶层定义可以当做特定文件的静态属性来使用
fun test() {println(Hello World)
}在编译之后它本质上就是一个Java中的静态方法而对应类的名称就是源文件名称Kt这里Main.kt对应的名称就是MainKt了
public static void main(String[] args) {MainKt.test();
}我们也可以使用注解来明确生成的Java字节码文件名称
file:JvmName(LBWNB)public static void main(String[] args) {LBWNB.test();
}对于伴生对象以及单例对象在Java中使用起来可能会有些别扭
class Student {companion object {fun test() {}}
}object Test {fun hello() {}
}public static void main(String[] args) {Student.Companion.test();Test.INSTANCE.hello();
}这并不是我们希望的样子在Kotlin中我们可以直接使用Student.的形式来直接调用而在Java中却有一些出入我们可以为这些函数添加JvmStatic注解来完成
class Student {companion object {JvmStatic fun test() {}}
}对于伴生对象中的字段我们也可以为其添加JvmField注解来使得其可以直接使用这会在编译时使得此函数作为Student的静态属性存在
public static void main(String[] args) {Student.test();
}同样的对于伴生对象中的属性来说我们也像上一小节那样添加JvmField注解
class Student {companion object {JvmField var name: String }
}public static void main(String[] args) {Student.name ; //此时name就是Student的静态属性
}