訪問 Kotlin 屬性
訪問包級(jí)別成員
- 使用 文件名 訪問
- 使用 @file:JvmName("自定義名") 訪問
實(shí)例字段铭拧、靜態(tài)字段和靜態(tài)函數(shù)
- 實(shí)例字段
- 靜態(tài)字段
- 靜態(tài)函數(shù)
可見性
生成重載函數(shù)
異常檢查
??Java 調(diào)用 Kotlin 要比 Kotlin 調(diào)用 Java 要麻煩一些,但還是比較容易實(shí)現(xiàn)的锚扎。
一担忧、訪問 Kotlin 屬性
??Kotlin 的一個(gè)屬性對(duì)應(yīng) Java 中的一個(gè)私有字段、一個(gè) setter
函數(shù) 和 一個(gè) getter
函數(shù)养匈,如果是只讀屬性則沒有 setter
函數(shù)哼勇。那么 Java 訪問 Kotlin 的屬性是通過這些 getter
函數(shù) 和 setter
函數(shù)。
// User.kt
data class User(val name: String, var password: String)
// MainJava.java
public class MainJava {
public static void main(String[] args) {
User user = new User("小三", "123456");
System.out.println(user.getName());
System.out.println(user.getPassword());
user.setPassword("abcdef");
System.out.println(user.getPassword());
}
}
// 運(yùn)行結(jié)果
小三
123456
abcdef
Process finished with exit code 0
??var
聲明的屬性會(huì)生成 getter
和 setter
兩個(gè)函數(shù)呕乎,但 val
聲明的屬性是只讀的积担,所以只會(huì)生成 getter
一個(gè)函數(shù)。
二猬仁、訪問包級(jí)別成員
??在同一個(gè) Kotlin 文件中帝璧,那些 頂層屬性和函數(shù) (包括頂層擴(kuò)展屬性和函數(shù)) 都不隸屬于某個(gè)類,但它們 隸屬于該 Kotlin 文件中定義的包湿刽。在 Java 中訪問它們時(shí)的烁,把它們當(dāng)成靜態(tài)成員。
-
使用 文件名 訪問
// 1?? 代碼文件:kot/kotlin_module/src/main/java/cn/ak/kotmodule/kot/topper.kotlin.kt
// 1?? 文件名為:topper.kotlin.kt
package cn.ak.kotmodule.kot
// 頂層函數(shù)
fun rectangleArea(width: Double, height: Double): Double = width * height
// 頂層屬性
val area = 100.0
// 頂層擴(kuò)展函數(shù)
fun User.printInfo() = println("{name=$name, password=$password}")
??上述是一個(gè)名為 topper.kotlin.kt
的 kotlin 源代碼文件诈闺,見第1??行注釋說明渴庆。文件 topper.kotlin.kt
中聲明了一個(gè) 頂層函數(shù)、一個(gè) 頂層屬性 和 一個(gè) User
的 擴(kuò)展函數(shù)雅镊。topper.kotlin.kt
文件編譯后會(huì)生成一個(gè) Topper_kotlinKt.class
文件襟雷,因?yàn)?點(diǎn)(.)
字符不能構(gòu)成 Java 類名,編譯器會(huì)將其 替換 為 下劃線(_)
仁烹,所以在 Java 中訪問 topper.kotlin.kt
對(duì)應(yīng)的類名是 Topper_kotlinKt
耸弄,見下面調(diào)用案例的第2??~4??行代碼。
// MainJava.java
public class MainJava {
public static void main(String[] args) {
// 訪問頂層函數(shù)
Double area = Topper_kotlinKt.rectangleArea(100, 50); // 2??
System.out.println(area);
// 訪問頂層屬性
System.out.println(Topper_kotlinKt.getArea()); // 3??
// 訪問擴(kuò)展函數(shù)
User user = new User("小三", "Lawrence");
Topper_kotlinKt.printInfo(user); // 4??
}
}
-
使用 @file:JvmName("自定義名") 訪問
??如果你覺得上面使用 文件名 來調(diào)用函數(shù)和屬性不夠友好卓缰,但還不想修改 Kotlin 源文件名计呈,那么可以在 Kotlin 源文件中使用 @JvmName
注解,指定生成的文件名征唬,如下面代碼第1??行震叮。
@file:JvmName("ExKotlin") //1??
package cn.ak.kotmodule.kot
// 頂層函數(shù)
fun rectangleArea(width: Double, height: Double): Double = width * height
// 頂層屬性
val area = 100.0
// 頂層擴(kuò)展函數(shù)
fun User.printInfo() = println("{name=$name, password=$password}")
??注意:@JvmName
注解必須放在 文件的第一行,否則會(huì)報(bào)編譯錯(cuò)誤鳍鸵。
// MainJava.java
public class MainJava {
public static void main(String[] args) {
// 訪問頂層函數(shù)
Double area = ExKotlin.rectangleArea(100, 50);
System.out.println(area);
// 訪問頂層屬性
System.out.println(ExKotlin.getArea());
// 訪問擴(kuò)展函數(shù)
User user = new User("小三", "Lawrence");
ExKotlin.printInfo(user);
}
}
三苇瓣、實(shí)例字段、靜態(tài)字段和靜態(tài)函數(shù)
??Java 語言中所有的變量和函數(shù)都被封裝到一個(gè)類中偿乖,類中包括實(shí)例函數(shù)(實(shí)例方法)击罪、實(shí)例屬性(成員變量)哲嘲、靜態(tài)屬性(靜態(tài)成員變量) 和 靜態(tài)函數(shù)(靜態(tài)方法),即:Java 中所有東西都放在類中 class 類名 {...}
媳禁。
-
實(shí)例字段
??如果需要以 Java 實(shí)例成員變量形式(即:實(shí)例名.成員變量)訪問 Kotlin 中的屬性眠副,則需要在該屬性前加 @JvmField
注釋,表明該屬性被當(dāng)作 Java 中的成員變量使用竣稽,訪問可見性相同囱怕。另外,延遲初始化(lateinit
) 屬性在 Java 中被當(dāng)作成員變量使用毫别,訪問可見性相同娃弓。
// kotlin
class Person {
// 姓名
@JvmField // 1??
val name = "小三"
// 年齡
var age = 20
// 生日
lateinit var birthDate: Date // 2??
}
// java
public class MainJava {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name); // 3??
System.out.println(person.getAge()); // 看不到屬性person.age
System.out.println(person.birthDate); // 4??
}
}
??kotlin 源碼中第1??行使用 @JvmField
注釋聲明 name
屬性,所以代碼第3??行可以直接訪問岛宦。kotlin 源碼第2??行 birthDate
聲明為延時(shí)屬性台丛,而延時(shí)屬性在 Java 源碼中也可以直接訪問。
-
靜態(tài)字段
??如果需要以 Java 靜態(tài)成員變量形式(即:類名.靜態(tài)成員變量) 訪問 Kotlin 中的屬性砾肺,可以有兩種實(shí)現(xiàn)方法:
??(1) 屬性聲明為頂層屬性挽霉,Java 中將所有的頂層成員(屬性和函數(shù))都認(rèn)為是靜態(tài)的。
// kotlin
@file:JvmName("ExKotlin")
// 頂層屬性
@JvmField
val area = 100.0
const val MAX_COUNT = 100
??(2) 在 Kotlin 的聲明對(duì)象和伴生對(duì)象中定義屬性变汪,這些屬性需要使用 @JvmField
注解侠坎、lateinit
或 const
來修飾。什么是伴生對(duì)象裙盾?click me 实胸!
// kotlin
object Singleton {
@JvmField
val x = 10
const val y = 100
lateinit var birthDate: Date
}
class Size {
var w = defaultHeight
var h = 0
companion object {
@JvmField
var defaultHeight = 100
const val defaultWith = 100
lateinit var defaultSize: Size
}
}
調(diào)用上述示例代碼:
// java
public class MainJava {
public static void main(String[] args) {
System.out.println("--------------------");
System.out.println(ExKotlin.area);
System.out.println(ExKotlin.MAX_COUNT);
System.out.println("--------------------");
System.out.println(Singleton.birthDate);
System.out.println(Singleton.x);
System.out.println(Singleton.y);
System.out.println("--------------------");
System.out.println(Size.defaultWith);
System.out.println(Size.defaultHeight);
System.out.println(Size.defaultSize);
}
}
// 運(yùn)行結(jié)果:
--------------------
100.0
100
--------------------
null
10
100
--------------------
100
100
null
Process finished with exit code 0
-
靜態(tài)函數(shù)
??如果需要以 Java 靜態(tài)方法形式(即:類名.方法名) 訪問 Kotlin 中的函數(shù),可以有兩種實(shí)現(xiàn)方法:
??(1) 函數(shù)聲明為頂層函數(shù)闷煤,Java 中將所有 kotlin中的頂層函數(shù)(屬性和函數(shù))都認(rèn)為是靜態(tài)的童芹。
// kotlin
@file:JvmName("ExKotlin")
// 頂層函數(shù)
fun rectangleArea(width: Double, height: Double): Double = width * height
??(2) 在 Kotlin 的聲明對(duì)象(object) 和 伴生對(duì)象(companion object) 中定義函數(shù)涮瞻,這些函數(shù)需要使用 @JvmStatic
來修飾鲤拿。什么是伴生對(duì)象?click me
// kotlin
object Singleton {
@JvmField
var x = 10
@JvmStatic
fun printlnX() = println("x=$x")
}
class Area {
companion object {
@JvmField
var defaultWith = 100
@JvmStatic
fun areaSize(height: Double) = defaultWith * height
}
}
調(diào)用上述示例代碼:
// java
public class MainJava {
public static void main(String[] args) {
System.out.println("---------- 頂層函數(shù) ----------");
System.out.println(ExKotlin.rectangleArea(100, 20));
System.out.println("--------------------");
Singleton.printlnX();
System.out.println("--------------------");
System.out.println(Area.areaSize(20));
}
}
---------- 頂層函數(shù) ----------
2000.0
--------------------
x=10
--------------------
2000.0
Process finished with exit code 0
四署咽、可見性
??Java 和 Kotlin 都有4種可見性近顷,但是除了 public
可以完全兼容外,其他的可見性都是有所區(qū)別的宁否。
-
Java 可見性窒升,默認(rèn)為包私有
默認(rèn)為包私有 Kotlin 可見性
可見性 | 修飾符 | 類成員聲明 | 頂層聲明 | 說明 |
---|---|---|---|---|
公有 | public | 所有地方可見 | 所有地方可見 | public是默認(rèn)修飾符 |
內(nèi)部 | internal | 模塊中可見 | 模塊中可見 | 不同于java中的包 |
保護(hù) | protected | 子類中可見 | 頂層聲明中不能使用 | |
私有 | private | 類中可見 | 文件中可見 |
??注意:kotlin 中沒有 Java 的包私有可見性,而具有模塊可見性 (internal)慕匠。
將 Java 可見性 和 Kotlin 可見性對(duì)比饱须,可知 Kotlin 中沒有默認(rèn)包私有可見性,而 Java 中沒有內(nèi)部可見性台谊。
(1) Kotlin 私有可見性
??由于 Kotlin 私有可見性可以聲明類中成員蓉媳,也可以聲明頂層成員譬挚。那么映射到 Java 分為兩種情況:
??a、Kotlin 類中私有成員映射到 Java 類中私有實(shí)例成員酪呻。
??b减宣、Kotlin 中私有頂層成員映射到 Java 中私有靜態(tài)成員。
(2) Kotlin 內(nèi)部可見性
??由于 Java 中沒有內(nèi)部可見性玩荠,那么 Kotlin 內(nèi)部可見性映射為 Java 公有可見性漆腌。
(3) Kotlin 保護(hù)可見性
??Kotlin 保護(hù)可見性映射為 Java 保護(hù)可見性。
(4) 公有可見性
??Kotlin 公有可見性映射為 Java 公有可見性阶冈。示例代碼
// KotlinSeeable.kt
internal class Emplyee {
internal var no: Int = 10 // 內(nèi)部可見性Java端可見
protected var job: String? = null // 保護(hù)可見性Java端子類繼承可見
private var salary: Double = 0.0 // 私有可見性Java端不可見
set(value) {
if (value >= 0.0) field = value
}
lateinit var dept: Department // 公有可見性Java端可見
}
open class Department {
protected var no: Int = 0 // 保護(hù)可見性Java端子類繼承可見
var name: String = "" // 公有可見性Java端可見
}
internal const val MAX_IN_COUNT = 300 // 內(nèi)部可見性Java端可見
private const val MIN_IN_COUNT = 0 // 私有可見性Java端不可見
??注意:Kotlin 中 屬性闷尿、函數(shù) 和 類 默認(rèn)可見性為public
。當(dāng) 函數(shù)眼溶、類 沒有被 open
修飾(即:默認(rèn)情況)是可不被繼承狀態(tài)悠砚。
- 通過 IDE 解釋后的 Java 代碼如下:
public final class Emplyee {
private int no = 10;
@Nullable
private String job;
private double salary;
@NotNull
public Department dept;
public final int getNo$kotlin_module() {
return this.no;
}
public final void setNo$kotlin_module(int var1) {
this.no = var1;
}
@Nullable
protected final String getJob() {
return this.job;
}
protected final void setJob(@Nullable String var1) {
this.job = var1;
}
private final void setSalary(double value) {
if (value >= 0.0D) {
this.salary = value;
}
}
@NotNull
public final Department getDept() {
Department var10000 = this.dept;
if (var10000 == null) {
Intrinsics.throwUninitializedPropertyAccessException("dept");
}
return var10000;
}
public final void setDept(@NotNull Department var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.dept = var1;
}
}
public class Department {
private int no;
@NotNull
private String name = "";
protected final int getNo() {
return this.no;
}
protected final void setNo(int var1) {
this.no = var1;
}
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name = var1;
}
}
public final class KotlinSeeableKt {
public static final int MAX_IN_COUNT = 300;
private static final int MIN_IN_COUNT = 0;
}
??注意:當(dāng) Kotlin 類函數(shù)被 Java 解釋時(shí),Kotlin 屬性被映射為 Java 的成員變量堂飞,其修飾符均為 private
灌旧,但其 getter
和 setter
方法的可見性會(huì)與 Kotlin 中屬性可見性對(duì)應(yīng)。lateint
修飾是個(gè)例绰筛,lateinit
修飾可見性保持一致枢泰。
- Java 調(diào)用 Kotlin 示例
public class MainJava {
public static void main(String[] args) {
Emplyee emp = new Emplyee();
// 訪問kotlin中內(nèi)部可見性的Emplyee成員屬性no
int no = emp.getNo$kotlin_module();
Department dept = new Department();
// 訪問kotlin中公有可見性的Department成員屬性name
dept.setName("市場(chǎng)部");
// 訪問Kotlin中公有可見性的Employee成員屬性dept
emp.setDept(dept);
System.out.println(emp.getDept());
// 訪問kotlin中內(nèi)部可見性的頂層屬性MAX_IN_COUNT
System.out.println(KotlinSeeableKt.MAX_IN_COUNT);
}
}
五、生成重載函數(shù)
??Kotlin 的函數(shù)參數(shù)可以設(shè)置默認(rèn)值铝噩,看起來像多個(gè)函數(shù)重載一樣衡蚂。但 Java 中并不支持參數(shù)默認(rèn)值,只能支持全部參數(shù)函數(shù)骏庸。為了解決這個(gè)問題毛甲,可以在 Kotlin 函數(shù)前使用 @JvmOverloads
注解,Kotlin 編譯器會(huì)生成多個(gè)重載函數(shù)具被。@JvmOverloads
注解的函數(shù)可以是 構(gòu)造函數(shù)玻募、成員函數(shù) 和 頂層函數(shù),但 不能是抽象函數(shù)一姿。
- kotlin 示例代碼:
class Animal @JvmOverloads constructor(val age: Int, val sex: Boolean = false)
class DisplayOverloading {
@JvmOverloads
fun display(c: Char, num: Int = 1) {
println("$c $num")
}
}
@JvmOverloads
fun makeCoffee(type: String = "卡布奇諾"): String {
return "制作一杯${type}咖啡"
}
- IDE 解釋 Java 代碼:
public final class KotlinOverloadsKt {
@JvmOverloads
@NotNull
public static final String makeCoffee(@NotNull String type) {
Intrinsics.checkParameterIsNotNull(type, "type");
return "制作一杯" + type + "咖啡";
}
// $FF: synthetic method
@JvmOverloads
@NotNull
public static String makeCoffee$default(String var0, int var1, Object var2) {
if ((var1 & 1) != 0) {
var0 = "卡布奇諾";
}
return makeCoffee(var0);
}
@JvmOverloads
@NotNull
public static final String makeCoffee() {
return makeCoffee$default((String)null, 1, (Object)null);
}
}
// DisplayOverloading.java
public final class DisplayOverloading {
@JvmOverloads
public final void display(char c, int num) {
String var3 = "" + c + ' ' + num;
boolean var4 = false;
System.out.println(var3);
}
// $FF: synthetic method
@JvmOverloads
public static void display$default(DisplayOverloading var0, char var1, int var2, int var3, Object var4) {
if ((var3 & 2) != 0) {
var2 = 1;
}
var0.display(var1, var2);
}
@JvmOverloads
public final void display(char c) {
display$default(this, c, 0, 2, (Object)null);
}
}
// Animal.java
public final class Animal {
private final int age;
private final boolean sex;
public final int getAge() {
return this.age;
}
public final boolean getSex() {
return this.sex;
}
@JvmOverloads
public Animal(int age, boolean sex) {
this.age = age;
this.sex = sex;
}
// $FF: synthetic method
@JvmOverloads
public Animal(int var1, boolean var2, int var3, DefaultConstructorMarker var4) {
if ((var3 & 2) != 0) {
var2 = false;
}
this(var1, var2);
}
@JvmOverloads
public Animal(int age) {
this(age, false, 2, (DefaultConstructorMarker)null);
}
}
??通過 IDE 解釋后七咧,可以看出 Kotlin 中的參數(shù)默認(rèn)值,在 Java 中實(shí)際是通過函數(shù)的重載來實(shí)現(xiàn)的叮叹。
- Java 調(diào)用 Kotlin 含有默認(rèn)參數(shù)的函數(shù):
public class MainJava {
public static void main(String[] args) {
Animal animal1 = new Animal(10, true);
Animal animal2 = new Animal(10);
DisplayOverloading dis1 = new DisplayOverloading();
dis1.display('A');
dis1.display('B', 20);
KotlinOverloadsKt.makeCoffee();
KotlinOverloadsKt.makeCoffee("摩卡咖啡");
}
}
注意:別忘了上面的 @file:JvmName("自定義名")
的使用艾栋。
六、異常檢查
??Kotlin 中沒有受檢查異常蛉顽,在函數(shù)后面也不會(huì)有異常聲明蝗砾。如果有如下的 Kotlin 代碼:
@file:JvmName("ErrorKt")
package cn.ak.kotmodule.kot
import java.text.SimpleDateFormat
import java.util.*
fun readDate(): Date? {
val str = "201D-18-19"
val df = SimpleDateFormat("yyyy-MM-dd") // 1??拋出異常
// 從字符串中解析日期
return df.parse(str)
}
??上面代碼第1??行會(huì)拋出 ParseException
異常,這是因?yàn)榻馕龅淖址皇且粋€(gè)合法的日期。在 Java 中 ParseException
是受檢查異常悼粮,如果在 Java 中調(diào)用 readDate
函數(shù)拇泣,由于 readDate
函數(shù)沒有聲明拋出 ParseException
異常,編譯器不會(huì)檢查要求 Java 程序捕獲異常處理矮锈。Java 調(diào)用代碼如下:
public class MainJava {
public static void main(String[] args) {
ErrorKt.readDate();
}
}
??這樣處理異常不符合 Java 的習(xí)慣霉翔,為此可以在 Kotlin 的函數(shù)前加上 @Throw
注解,修改 Kotlin 代碼如下:
@Throws(ParseException::class)
fun readDate(): Date? {
val str = "201D-18-19"
val df = SimpleDateFormat("yyyy-MM-dd") // 拋出異常
// 從字符串中解析日期
return df.parse(str)
}
??注意在 readDate
函數(shù)前添加注解 @Throws(ParseException::class)
苞笨,其中 ParseException
是需要處理的異常類债朵。
??那么 Java 代碼可以修改為如下捕獲異常形式:
public class MainJava {
public static void main(String[] args) {
try {
ErrorKt.readDate();
} catch (ParseException e) {
e.printStackTrace();
}
}
}
??當(dāng)然在 Java 中除了 try-catch
捕獲異常,還可以聲明拋出異常瀑凝。