Java 方法在 kotlin 中错误地自动重载
Java methods wrongly get automatically overloaded in kotlin
给定一个 Java 库,其中包含以下内容(精简)class:
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)
由于库还提供了将结果写入另一个向量的重载,我想知道我是否可以 'tell' 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!
理想情况下,您可以更改 Java class 以使其运算符函数更好地遵守约定。如果您无法修改原始 class,您将始终可以在使用 Kotlin 时使用 /
运算符。
因为它在原件中 class,您将无法用扩展函数覆盖它,原件将始终优先于它,无论扩展在哪里声明或如何导入.
您要么必须接受可用的语法,要么如果您真的不能使用它,您可以围绕 Vector2f
创建一个包装器 class,它没有该名称的功能公开可用。
我正在 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)
背后的逻辑很简单,参考你的样本,shortest/simplest代码:
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_
解释为 /=
给定一个 Java 库,其中包含以下内容(精简)class:
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)
由于库还提供了将结果写入另一个向量的重载,我想知道我是否可以 'tell' 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!
理想情况下,您可以更改 Java class 以使其运算符函数更好地遵守约定。如果您无法修改原始 class,您将始终可以在使用 Kotlin 时使用 /
运算符。
因为它在原件中 class,您将无法用扩展函数覆盖它,原件将始终优先于它,无论扩展在哪里声明或如何导入.
您要么必须接受可用的语法,要么如果您真的不能使用它,您可以围绕 Vector2f
创建一个包装器 class,它没有该名称的功能公开可用。
我正在 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)
背后的逻辑很简单,参考你的样本,shortest/simplest代码:
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_
解释为 /=