Kotlin属性访问器与方法签名冲突的问题

简介: Kotlin属性访问器与方法签名冲突的问题

是什么问题?

如果你熟悉Java语言,那么你就会了解下面这个代码片段是不被允许的:

class SomeClass {
  public int getData() { /* ... */ } // 'getData()' clashes with 'getData()'; both methods have same erasure
  public String getData() { /* ... */ }
}

如果使用Kotlin编写,同样也是无法通过的:

class SomeClass {
  fun getData(): Int { /* ... */ } // Conflicting overloads ...
  fun getData(): String { /* ... */ }
}

但是,如果 SomeClass 这个类有一个属性是 data,那么情况就有点不一样:

// ok
class SomeClass {
  val data: Int = 0
  fun getData(): String { /* ... */ }
}

上面这种写法是被允许的,因为当我们使用 SomeClass 的实例区分别访问属性和方法时,访问方式并不一样:

val instance = SomeClass()
val intData : Int = instance.data
val stringData : String = instance.getData()

到这里,一切都合情合理。但如果我们看一下Kotlin反编译后的Java代码,会得到这样的结果:

public class SomeClass {
   private final int data;
   public final int getData() {
      return this.data;
   }
   @NotNull
   public final String getData() {
      return "";
   }
}

这么看就有点奇怪了,因为出现了上面示例中不允许的代码。

什么原因呢?

这个问题有三个点值得我们关注:

  1. 在Kotlin语法中,对属性的访问和对getXxx的访问并不是等价的。(虽然kotlin编译器可以自动把对Java getter的访问转换成属性访问)
  2. Java/Kotlin编译器的规则并不等于JVM的规则。
  3. 使用IDE查看Kotlin编译成的字节码反编译后的Java代码,不代表能通过Java编译器的编译。

1

第一个很好理解,对Kotlin编译器来说,这两者并不存在签名冲突,所以是合法的。

2

编译器的作用在于将文本代码编译成字节码,供JVM执行。”方法签名冲突“这个错误是编译器报告的错误,而不是JVM。

JVM在执行期间,对方法的调用是通过内存地址而不是方法签名,所以也就没有冲突的问题。

3

Kotlin会被编译成JVM字节码,而不是Java代码。反编译得到的Java代码不一定能通过Java编译器的编译。

总结

虽然Kotlin允许属性和getter方法同时存在,但是并不建议这样对属性和方法进行命名。首先命名上存在歧义;其次,如果在Java代码中调用这两个方法,Java编译器将会报错。因为Java编译器无法分data属性的getter方法和getData方法,导致无法通过编译。

看完原因再反过来看上面的问题,就会觉得这并不是个问题。但回想一下遇到这个问题时没有理解的原因,应该还是对Kotlin的理解出现了偏差。从Java转到Kotlin之后,很多时候还是会尝试从Java的角度来理解Kotlin,所以经常会忽略掉一个关键点:Kotlin基于JVM,而非Java

以这个角度作为前提,才能更 “Writing Kotlin the Kotlin Way” 吧。

虽然经常从 stackoverflow.com 上找到问题的答案,但是从来没有提问或者回答过,遇到这个问题(最开始以为和继承有关系)搜索无果后,尝试在stackoverflow上提了个问题,结果很快就得到了回答。深切感受到了开发者的友善。”同一个世界,同一份代码“~ 感兴趣的同学可以去看下原答案。

相关文章
|
4月前
|
安全 Java Kotlin
面试必备:Kotlin 线程同步的 N 种方法
面试必备:Kotlin 线程同步的 N 种方法
84 0
|
5月前
|
Kotlin
kotlin获取属性注解
kotlin获取属性注解
51 0
|
7月前
|
Cloud Native Go Kotlin
Kotlin 环境下解决属性初始化问题
Kotlin 环境下解决属性初始化问题
28 0
|
9月前
|
Java Kotlin
Kotlin中与Java互操作与可空性、类型映射、属性访问、@JvmOverloads、@JvmField、@JvmStatic、@Throws和函数类型操作详解
Kotlin中与Java互操作与可空性、类型映射、属性访问、@JvmOverloads、@JvmField、@JvmStatic、@Throws和函数类型操作详解
70 0
|
JSON 安全 Java
Kotlin学历之委托属性
Kotlin学历之委托属性
99 0
Kotlin学历之委托属性
|
Java Kotlin
Kotlin去掉UUID 横杠 方法
简简单单,一行代码搞定: val uuid = UUID.randomUUID().toString().replace("-", “”) 如果在Java中也可以用 UUID.randomUUID().toString().replaceAll("-", “”)
234 0
Kotlin去掉UUID 横杠 方法
|
安全 Java Kotlin
面试必备:Kotlin 线程同步的 N 种方法
面试的时候经常会被问及多线程同步的问题,例如,有 Task1、Task2 等多个并行任务,如何等待全部执行完成后执行 Task3?
589 0
|
Java Kotlin
【Kotlin 初学者】函数(方法)(下)
函数 函数(function)和方法(method)这两个概念经常被混淆,他们到底有什么关联和区别?其实,它俩没有什么区别只是不同语言叫法不一样,在Java中普遍叫方法。而在Kotlin中普遍叫函数。重点是:函数(function)和方法(method)是同一个东西,别搞错了。
128 0
【Kotlin 初学者】函数(方法)(下)
|
Java Android开发 Kotlin
【Kotlin 初学者】函数(方法)(上)
目录 函数 函数头 实践 函数参数 实践 Unit函数 Nothing类型 实践 反引号中的函数名 使用空格和特殊字符对函数命名 实践 Kotlin和Java互操作
173 0
【Kotlin 初学者】函数(方法)(上)
|
Java Kotlin
【Kotlin】变量简介 ( 可空类型 | lateinit | 初始化判定 | 非空类型 | !! 与 ? 修饰符 | ?= ?. ?: 运算符 | 抽象属性变量)(二)
【Kotlin】变量简介 ( 可空类型 | lateinit | 初始化判定 | 非空类型 | !! 与 ? 修饰符 | ?= ?. ?: 运算符 | 抽象属性变量)(二)
191 0