在rust中,我们可以做这样的事情:
trait MyTrait {
fn my_func();
}
struct MyStruct {}
impl MyTrait for MyStruct {
fn my_func() {
// this gets invoked
}
}
fn my_func<B: MyTrait>() {
// This here
B::my_func();
}
fn main() {
my_func::<MyStruct>();
}
在java中,我们不能做这样的事情:
interface MyInterface {
public static void myFunc() {
// This gets invoked
}
}
class MyClass implements MyInterface {
public static void myFunc() {
}
}
public class Main {
static <T extends MyInterface> void myFunc() {
// This here
T.myFunc();
}
public static void main(String[] args) {
Main.<MyClass>myFunc();
}
}
现在还有其他问题关于为什么 java 不允许在 java 中覆盖/强制执行静态方法,但是这个 特定示例 是否因为 单态化与类型擦除而不起作用 >?我试图了解更深层次的原因。 我的猜测是因为类型擦除后只有一个实现,并且需要编译静态调用,所以它不起作用。这是真正的原因吗?
答案 0 :(得分:2)
正如您所指出的,类型擦除发生并且 Java 泛型可以被视为语法糖。它用于类型检查,但在 resulting byte code, it will be replaced 中。
如果是未绑定类型,则替换为Object
,但在您的示例中,它是绑定类型(T extends MyInterface
),因此将替换为MyInterface
反而。这就是调用 MyInterface.myFunc
的原因。
为什么编译器不像在 Rust 或 C++ 中那样解析它?我认为这是可能的,所以我只能推测 Java 中的设计决策。 Java 本质上比 Rust 更具动态性。新类可以在注释处理期间定义,但也可以动态创建。该属性在编译时分辨率方面表现不佳,因为编译器在编译时无法了解所有类。
Java 主要是一种面向对象的语言,而 Rust 和 C++ 更强调编译阶段。尽管 Java 是静态类型的,但它更具动态性,并且更多是在运行时完成的。这使它更灵活,但牺牲了性能。
表达您提供的 Rust 代码的“Java 方式”是使用普通函数而不是 static
函数。效果是相似的,但你最终会得到动态调度。 Rust like C++ 遵循零开销原则,因此被迫使用动态调度会违反该原则。另一方面,在 Java 中,它不是设计目标。
通常,JIT 仍然应该能够对其进行优化(即消除动态调度并内联代码)。但是没有像在等效的 Rust(或 C++)中那样保证,在编译时决定一切。
答案 1 :(得分:1)
我想明确说明 static
在 java 中不是继承的。
因此,该方法需要一个实例化,无论是单例。
interface MyInterface {
public void myFunc() {
// This gets invoked
}
}
class MyClass implements MyInterface {
@Override // Does not work with static.
public void myFunc() {
}
}
public class Main {
static void myFunc(MyInterface obj) {
// This here
obj.myFunc();
}
public static void main(String[] args) {
Main.myFunc(new MyClass());
}
}
您可以隐藏实例 obj
,使其成为 static
或其他。
可能 lambdas 是更好的解决方案:
Main.myFunc(MyClass::myStaticFunc);