Java中的Kotlin?內(nèi)部類原理
Java 中的內(nèi)部類
這是一個(gè) Java 內(nèi)部類的簡(jiǎn)單實(shí)現(xiàn):
public class OutterJava {
? ?private void printOut() {
? ? ? ?System.out.println("AAA");
? }
?
? ?class InnJava {
? ? ? ?public void printInn() {
? ? ? ? ? ?printOut();
? ? ? }
? }
}外部類是一個(gè)私有方法,內(nèi)部類為什么可以訪問(wèn)到外部類的私有方法呢?思考這個(gè)問(wèn)題,首先要從它的字節(jié)碼入手,看看 JVM 到底對(duì) java 文件做了什么。
字節(jié)碼分析流程是:
javac xxx.java生成 class 文件。javap -c xxx.class對(duì)代碼進(jìn)行反匯編,可以生成可查看的代碼內(nèi)容。
通過(guò) javac 命令生成 class 文件,此時(shí)會(huì)發(fā)現(xiàn)生成了兩個(gè) class 文件,一個(gè)外部類 OtterJava 的,一個(gè)內(nèi)部類 InnJava 的。
OutterJava.class
OutterJava.class 反匯編后的代碼如下所示,這里面除了一個(gè)構(gòu)造方法,多生成了一個(gè)
Compiled from "OutterJava.java"
public class java.OutterJava {
?public java.OutterJava();
? ?Code:
? ? ? 0: aload_0
? ? ? 1: invokespecial #2 ? ? ? ? ? ? ? ? ?// Method java/lang/Object."<init>":()V
? ? ? 4: return
?
?private void printOut();
? ?Code:
? ? ? 0: getstatic ? ? #3 ? ? ? ? ? ? ? ? ?// Field java/lang/System.out:Ljava/io/PrintStream;
? ? ? 3: ldc ? ? ? ? ? #4 ? ? ? ? ? ? ? ? ?// String AAA
? ? ? 5: invokevirtual #5 ? ? ? ? ? ? ? ? ?// Method java/io/PrintStream.println:(Ljava/lang/String;)V
? ? ? 8: return
?
?static void access$000(java.OutterJava);
? ?Code:
? ? ? 0: aload_0
? ? ? 1: invokespecial #1 ? ? ? ? ? ? ? ? ?// Method printOut:()V
? ? ? 4: return
}從反編譯出來(lái)的內(nèi)容來(lái)看,多了一個(gè)靜態(tài)的access$000(OutterJava)方法,它的內(nèi)部調(diào)用了 printOut()。
InnJava.class
Compiled from "OutterJava.java"
class java.OutterJava$InnJava {
?final java.OutterJava this$0;
?
?java.OutterJava$InnJava(java.OutterJava);
? ?Code:
? ? ? 0: aload_0
? ? ? 1: aload_1
? ? ? 2: putfield ? ? ?#1 ? ? ? ? ? ? ? ? ?// Field this$0:Ljava/OutterJava;
? ? ? 5: aload_0
? ? ? 6: invokespecial #2 ? ? ? ? ? ? ? ? ?// Method java/lang/Object."<init>":()V
? ? ? 9: return
?
?public void printInn2();
? ?Code:
? ? ? 0: aload_0
? ? ? 1: getfield ? ? ?#1 ? ? ? ? ? ? ? ? ?// Field this$0:Ljava/OutterJava;
? ? ? 4: invokestatic ?#3 ? ? ? ? ? ? ? ? ?// Method java/OutterJava.access$000:(Ljava/OutterJava;)V
? ? ? 7: return
}在 InnJava 的字節(jié)碼反編譯出來(lái)的內(nèi)容中,主要有兩個(gè)點(diǎn)需要注意:
- 構(gòu)造方法需要一個(gè)外部類參數(shù),并把這個(gè)外部類實(shí)例保存到了
this$0中。 - 調(diào)用外部類私有方法,實(shí)際上是調(diào)用了
OutterJava.access$000方法。
小結(jié):
在 Java 中,內(nèi)部類與外部類的關(guān)系是:
- 內(nèi)部類持有外部類的引用,作為內(nèi)部構(gòu)造參數(shù)傳入外部類實(shí)例,并保存到了內(nèi)部類的屬性
this$0中。 - 內(nèi)部類調(diào)用外部類的私有方法,實(shí)際上是外部類生成了內(nèi)部實(shí)際調(diào)用私有方法的靜態(tài)方法
access$000,內(nèi)部類可以通過(guò)這個(gè)靜態(tài)方法訪問(wèn)到外部類中的私有方法。
Kotlin 中的內(nèi)部類
同樣的 Java 代碼,用 Kotlin 實(shí)現(xiàn):
class Outter {
? ?private fun printOut() {
? ? ? ?println("Out")
? }
?
? ?inner class Inner {
? ? ? ?fun printIn() {
? ? ? ? ? ?printOut()
? ? ? }
? }
}這里如果不加inner關(guān)鍵字,printIn()內(nèi)的printOut()會(huì)報(bào)錯(cuò)Unresolved reference: printOut 。
不加inner關(guān)鍵字,反編譯后的字節(jié)碼:
public final class java/Outter$Inner {
// ...
public <init>()V
? L0
? LINENUMBER 8 L0
? ALOAD 0
? INVOKESPECIAL java/lang/Object.<init> ()V
? RETURN
? L1
? LOCALVARIABLE this Ljava/Outter$Inner; L0 L1 0
? MAXSTACK = 1
? MAXLOCALS = 1
// ...
}不加inner關(guān)鍵字,內(nèi)部類的構(gòu)造方法是沒(méi)有外部類實(shí)例參數(shù)的。如果加上inner,就和 Java 一樣:
// 加上了 inner 的構(gòu)造方法 public <init>(Ljava/Outter;)V ? L0 ? LINENUMBER 8 L0 ? ALOAD 0 ? ALOAD 1 ? PUTFIELD java/Outter$Inner.this$0 : Ljava/Outter; ? ALOAD 0 ? INVOKESPECIAL java/lang/Object.<init> ()V ? RETURN ? L1 ? LOCALVARIABLE this Ljava/Outter$Inner; L0 L1 0 ? LOCALVARIABLE this$0 Ljava/Outter; L0 L1 1 ? MAXSTACK = 2 ? MAXLOCALS = 2
而內(nèi)部類對(duì)于外部類私有方法的訪問(wèn),也是通過(guò)靜態(tài)方法access$XXX來(lái)實(shí)現(xiàn)的:
public final static synthetic access$printOut(Ljava/Outter;)V ? L0 ? LINENUMBER 3 L0 ? ALOAD 0 ? INVOKESPECIAL java/Outter.printOut ()V ? RETURN ? L1 ? LOCALVARIABLE $this Ljava/Outter; L0 L1 0 ? MAXSTACK = 1 ? MAXLOCALS = 1
總結(jié)
在 Kotlin 中,內(nèi)部類持有外部類引用和通過(guò)靜態(tài)方法訪問(wèn)外部類私有方法都是與 Java 一樣的。唯一的不同是,Kotlin 中需要使用 inner關(guān)鍵字修飾內(nèi)部類,才能訪問(wèn)外部類中的內(nèi)容。實(shí)質(zhì)是inner關(guān)鍵字會(huì)控制內(nèi)部類的構(gòu)造方法是否帶有外部類實(shí)例參數(shù)。
到此這篇關(guān)于Java中的Kotlin 內(nèi)部類原理的文章就介紹到這了,更多相關(guān)Java Kotlin 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java重寫(@Override)介紹以及舉例說(shuō)明
這篇文章主要給大家介紹了關(guān)于java重寫(@Override)介紹以及舉例說(shuō)明的相關(guān)資料,在Java中@Override注解用于表示方法重寫(覆蓋)了父類的方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
springMVC?@RestControllerAdvice注解使用方式
這篇文章主要介紹了springMVC?@RestControllerAdvice注解使用方式,下面通過(guò)一個(gè)簡(jiǎn)單的示例,演示如何使用?@RestControllerAdvice,感興趣的朋友跟隨小編一起看看吧2024-08-08
Maven搭建springboot項(xiàng)目的方法步驟
這篇文章主要介紹了Maven搭建springboot項(xiàng)目的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Java實(shí)現(xiàn)對(duì)稱加密DES和AES的示例代碼
這篇文章主要介紹了如何使用Java實(shí)現(xiàn)采用對(duì)稱密碼算法的應(yīng)用軟件,所用算法包括DES算法和AES算法,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2023-04-04
SpringCloud @FeignClient參數(shù)的用法解析
這篇文章主要介紹了SpringCloud @FeignClient參數(shù)的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
Java 通過(guò)位運(yùn)算求一個(gè)集合的所有子集方法
下面小編就為大家?guī)?lái)一篇Java 通過(guò)位運(yùn)算求一個(gè)集合的所有子集方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03
java通過(guò)url讀取遠(yuǎn)程數(shù)據(jù)并保持到本地的實(shí)例代碼
本文通過(guò)實(shí)例代碼給大家介紹了java通過(guò)url讀取遠(yuǎn)程數(shù)據(jù)并保持到本地的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-07-07
SpringBoot連接Hive實(shí)現(xiàn)自助取數(shù)的示例
這篇文章主要介紹了SpringBoot連接Hive實(shí)現(xiàn)自助取數(shù)的示例,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下2020-12-12
Springboot使用RabbitMQ實(shí)現(xiàn)關(guān)閉超時(shí)訂單(示例詳解)
介紹了如何在Spring Boot項(xiàng)目中使用RabbitMQ實(shí)現(xiàn)訂單的延時(shí)處理和超時(shí)關(guān)閉,通過(guò)配置RabbitMQ的交換機(jī)、隊(duì)列和綁定關(guān)系,以及編寫監(jiān)聽方法,實(shí)現(xiàn)了訂單數(shù)據(jù)的發(fā)送和延時(shí)消費(fèi),感興趣的朋友一起看看吧2025-01-01

