【问题标题】:Java methods wrongly get automatically overloaded in kotlinJava 方法错误地在 kotlin 中自动重载
【发布时间】:2017-03-08 08:49:58
【问题描述】:

给定一个包含以下(压缩)类的 Java 库:

public class Vector2f {
    public float x;
    public float y;

    public Vector2f div(Vector2f other) {
        x /= other.x;
        y /= other.y;
        return this;
    }

    public Vector2f div(Vector2f other, Vector2f dest) {
        dest.x = x / other.x;
        dest.y = y / other.y;
        return dest;
    }

    /* ... */
}

由于 kotlin 会自动将合适的方法名称转换为重载运算符,所以我可以编写

val v0 = Vector2f(12f, 12f)
val v1 = Vector2f(2f, 2f)

val res = v0 / v1

println(v0)
println(v1)
println(res)

res.x = 44f
println()

println(v0)
println(v1)
println(res)

...完全出乎意料的结果是v0 被中缀除法操作改变了。此外,存储在res 中的引用指向与v0 相同的对象输出:

Vector2f(6.0, 6.0)
Vector2f(2.0, 2.0)
Vector2f(6.0, 6.0)

Vector2f(44.0, 6.0)
Vector2f(2.0, 2.0)
Vector2f(44.0, 6.0)

由于库还提供了将结果写入另一个向量的重载,我想知道是否可以“告诉”kotlin 不要使用提供的Vector2f.div(Vector2f) 方法。

我已经尝试为Vector2f 提供一个扩展方法,但它被真正的成员所掩盖:

operator fun Vector2f.div(other: Vector2f): Vector2f = this.div(other, Vector2f())
                      ^~~ extension is shadowed by a member: public open operator fun div(other: Vector2f!): Vector2f!

【问题讨论】:

  • 如果有兴趣,here glm 端口
  • @elect 使用您的端口是最好的解决方案,您为什么不发布答案?
  • 怕显得过于坚持/侵略,但既然你建议了,我就试试..

标签: java operator-overloading interop kotlin


【解决方案1】:

我正在处理 glm 端口here

对于你的例子,相关代码是here

operator fun div(b: Float) = div(Vec2(), this, b, b)
operator fun div(b: Vec2) = div(Vec2(), this, b.x, b.y)

fun div(bX: Float, bY: Float, res: Vec2 = Vec2()) = div(res, this, bX, bY)
fun div(b: Float, res: Vec2 = Vec2()) = div(res, this, b, b)
fun div(b: Vec2, res: Vec2 = Vec2()) = div(res, this, b.x, b.y)

fun div_(bX: Float, bY: Float) = div(this, this, bX, bY)
infix fun div_(b: Float) = div(this, this, b, b)
infix fun div_(b: Vec2) = div(this, this, b.x, b.y)

背后的逻辑很简单,参考你的示例,最短/最简单的代码:

val v0 = Vec2(12f)
val v1 = Vec2(2f)

val res = v0 / v1

总是 创建一个新实例。这在某种程度上遵循了Kotlin docs 上也写的准则。 inc()dec() 部分仍然存在(同时已修改)。

也是尽可能少出错的形式(个人经验..)

对于性能关键的场景,当你不想分配新的实例时,折衷的办法是放弃算子重载,使用函数形式:

v0.div(v1, res)

这意味着,将v0 除以v1 并将结果放入res

如果您希望接收者对象直接改变和适应结果:

v0 div_ v1

这背后的想法是利用下划线_和等号=背后的相似性,并将div_解释为/=

【讨论】:

  • 我为这项工作投了赞成票,我肯定会调查您的 GLM 端口,而不是使用我现在拥有的端口。由于它没有提供问题的答案('我可以让 kotlin 接受我的运算符重载而不是自动生成的'),因此恐怕我不能接受它是正确的!
  • 是的,别担心,我完全明白了,感谢您的支持 :) 我希望它对与您有类似问题的人有用
【解决方案2】:

理想情况下,您应该更改 Java 类以使其运算符函数更好地遵守约定。如果您无法修改该原始类,则在使用 Kotlin 时始终可以使用 / 运算符。

由于它位于原始类中,因此您将无法使用扩展函数覆盖它,无论扩展在何处声明或如何导入,原始将始终优先于它。

你要么必须接受可用的语法,要么如果你真的不能这样,你可以围绕 Vector2f 创建一个包装类,它不具有该名称的公开功能。

【讨论】:

  • 这有点可悲,因为图书馆真的很棒。但是使用一个让你完全惊讶的运算符重载是很糟糕的:(
  • @kotlin.internal.HidesMembers 可以解决问题,但你知道,如果你为 JetBrains 工作..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多