为什么我无法在 Kotlin 中访问匿名对象的非私有成员?

huangapple go评论50阅读模式
英文:

Why do I can't access to non-private member of anonymous object in Kotlin?

问题

我是一个Java开发者,现在我正在学习Kotlin。

我遇到了一段有趣(有效)的代码:

class C {
    private fun getObject() = object {
        val x: String = "x"
    }

    fun printX() {
        println(getObject().x)
    }
}

但如果我移除getObject方法中的private修饰符:

class C {
    fun getObject() = object {
        val x: String = "x"
    }

    fun printX() {
        println(getObject().x)
    }
}

Kotlin会报错:

Unresolved reference: x

你能解释一下为什么要这样做吗?从我的角度来看,这看起来有点奇怪。

英文:

I am a java developer and now I am learning Kotlin.

I've met an interesting(working) piece of code:

class C {
    private fun getObject() = object {
        val x: String = "x"
    }

    fun printX() {
        println(getObject().x)
    }
}

But if I remove private modifier for getObject method:

class C {
    fun getObject() = object {
        val x: String = "x"
    }

    fun printX() {
        println(getObject().x)
    }
}

The Kotlin complains

Unresolved reference: x

Could you please explain why is it done this way ? From my point of view it looks a bit weird.

答案1

得分: 1

这是因为我们创建了一个匿名对象/类,所以它在用户可用的类型系统中没有表示。没有带有 x 属性的类,我们只能将此对象表示为 Any。当然,在内部,编译器会创建一个带有 x 的类,但是这个类对我们不可见,我们无法将这样的对象以其真实类型存储在属性中等。

那么为什么它能够工作呢?

Kotlin 和一些其他语言有时会为本地代码提供“超级功能”。有多个示例:

  • Kotlin:如上所述,访问 object 成员,但仅限于私有成员。
  • Kotlin:智能属性转换,但仅限于本地模块。
  • Java:类型推断,但仅适用于本地变量。
  • Kotlin/Java:交集类型,但仅适用于本地变量。
  • Kotlin/Java:访问嵌套/外部类的私有字段。

很难简单地回答为什么我们这样做的问题。但通常,当我们处理本地或私有代码时,我们关注的是文件或类的实现细节中的这个小功能片段。然后,我们通常更喜欢方便和隐式,而不是保持代码严格和显式。如果我们必须更改任何内容,我们会在一个地方进行更改。

如果组件是公共的,情况就相反了。在这种情况下,它们可以从代码中的多个地方使用,我们无法记住所有这些地方,因此如果我们保持代码更加严格,更容易维护,比如创建一个常规的、有名称的类,而不是匿名类。

如果从另一个模块访问代码,情况就会更糟。在这种情况下,编译器不知道原始代码的完整上下文,它只看到一个返回带有某个随机名称的类的方法。Kotlin 可以通过使用注解在字节码中添加额外的信息,但是 Java/JavaScript 将完全忽略它们。此外,由于我们没有为匿名类提供名称,编译器不得不自动生成它。如果将来生成不同的名称,这将是一个不兼容的二进制更改。

另一个潜在原因是编译器的复杂性。为了允许这些特性,编译器必须保留有关代码的附加元数据,并且必须应用一些“打破规则”的技巧。如果它们仅在本地允许,那么编译器可以一次为单个文件/类/模块执行这些复杂的转换,但在一般情况下保持更简单的代码表示。

英文:

Why it doesn't work?

This is because we created an anonymous object/class, so it doesn't have a representation in the type system available to the user. There is no class with x property, we can only represent this object as Any. Of course, internally the compiler creates a class with x, but this class is not visible to us, we can't store such object with its real type in a property, etc.

So why it does work?

Kotlin and some other languages sometimes provide "superpowers" for local code. There are multiple examples of this:

  • Kotlin: as above, access object members, but only if private.
  • Kotlin: smart cast of properties, but only if in a local module.
  • Java: type inference, but only for local vars.
  • Kotlin/Java: intersection types, but only for local vars.
  • Kotlin/Java: access private fields of nested/outer classes.

It's hard to provide a simple answer on why we do this. But generally, when we work on local or private code, then we focus on this small fragment of functionality which we consider implementation details of the file or class. Then we often prefer convenience and to be implicit, than keeping the code strict and explicit. If we have to change anything, we do this in a single place.

It is the opposite if components are public. In that case they could be used from tens of places around the code, we can't remember about all of them, so it is safer and easier to maintain if we keep our code more strict, so e.g. create a regular, named class instead of anonymous one.

It is even worse if accessing the code from another module. In that case the compiler doesn't know the full context of the original code, it only sees a method returning a class with some random name. Kotlin could add extra info in the bytecode by using annotations, but Java/JavaScript would ignore them entirely. Also, as we didn't provide a name for the anonymous class, compiler had to auto-generate it. If it'll generate a different name in the future, that will be a binary-incompatible change.

Another potential reason is the complexity for the compiler. To allow such features, the compiler has to keep additional metadata about the code and it has to apply some tricks to "break the rules". If they are allowed only locally, the compiler can do these complex transformations for a single file/class/module at a time, but keep a simpler code representation in general.

huangapple
  • 本文由 发表于 2023年6月29日 17:50:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76579934.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定