我一直在处理域问题,其中我有实现公共接口的类,我希望能够以不同方式从这些对象中获取哈希值,具体取决于对象是作为接口访问还是作为具体类实例访问。基本上我想要的是以下内容:
public class A implements Bar{
@Override
public int hashCode(){ return 1;}
@Override
public int Bar.hashCode(){ return 123;}
}
public class B implements Bar{
@Override
public int hashCode(){ return 1;}
@Override
public int Bar.hashCode(){ return 123;}
}
public class C implements Bar{
@Override
public int hashCode(){ return 1;}
@Override
public int Bar.hashCode(){ return 123;}
}
Bar interfaceObject = new A();
interfaceObject.hashCode(); //return 123
Bar aObject = new A();
aObject.hashCode();// return 1
据我所知,没有办法做到这一点,我可以想出为什么这可能会导致问题的很多原因,但是我想问那些比我更聪明的人,如果他们有任何好办法做到这一点在使界面具有类似public int getCustomHashCodeForJustThisInterface()
的功能之外。我喜欢能够在hashMaps中使用这些对象,而不必跳过篮球,但是使用hashCode的当前实现它们会破坏,因为这些对象可以根据它们的使用方式有多个身份视图,而且我不会想要改变hashCode的基本实现;
答案 0 :(得分:4)
你不能这样做,因为Java不支持非多态实例方法(静态方法不是多态的,如前面的答案所示)。
你可以做的是让你的课程不直接实现Bar
,而另一个界面(例如BarProvider
)使用toBar()
或getBar()
方法,返回类型为Bar
的自定义对象,其行为符合您的要求。
public class A implements BarProvider{
@Override
public int hashCode(){ return 1;}
@Override
public Bar toBar() {
return new Bar() {
@Override
public int hashCode() { return 123; }
};
}
}
A aObject = new A();
interfaceObject.hashCode(); //return 1;
Bar interfaceObject = aObject.toBar();
interfaceObject.hashCode(); // return 123
可以进行一些改进,例如将Bar
对象存储为最终字段(以避免多次初始化),并使用反向引用,允许您从Bar
返回到其BarProvider
。
另一种可能性是使用外部提供程序进行计算
public class A implements Bar{
@Override
public int hashCode(){ return 1;}
}
public final class BarHasher implements Hasher<Bar> }
@Override
public int hashFor(Bar object) { return 123; }
}
A aObject = new A();
interfaceObject.hashCode(); //return 1;
BarHasher.hashFor(aObject); // return 123
或调用其他方法的静态方法
public class A implements Bar{
@Override
public int hashCode(){ return 1;}
@Override
public int hashAsBar() { return 123; }
}
public interface BarHasher implements Hasher<Bar> {
@Override
public int hashFor(Bar object) { return object.hashAsBar(); }
}
A aObject = new A();
interfaceObject.hashCode(); //return 1;
BarHasher.hashFor(aObject); // return 123
如果您不知道它,那么您正在尝试做的事情(并且它是默认行为)在C ++中(您必须将方法声明为virtual
以具有与Java相同的行为)并且在C#中(但是你会收到警告,除非你在重写方法上使用修饰符new
)
答案 1 :(得分:2)
我知道没有办法做到这一点。
以下是你可能不知道的事情(我不暗示这是一个好主意):
package com.sandbox;
import java.io.IOException;
public class Sandbox {
public static void main(String[] args) throws IOException {
A.speak();
B.speak();
A a = new A();
a.speak(); //my ide rightly gives a warning here. static function accessed through instance
A b = new B();
b.speak(); //my ide rightly gives a warning here. static function accessed through instance
}
public static class A {
public static void speak() {
System.out.println("A");
}
}
public static class B extends A {
public static void speak() {
System.out.println("B");
}
}
}
这将打印:
A
B
A
A
重申:这不是一个好主意。我只是出于教育目的而告诉你。
答案 2 :(得分:2)
根据变量的声明的类型调用不同的方法很容易。这被称为覆盖,这是一个例子:
public class Example {
public static void main(String[] argv) throws Exception {
Integer v1 = 12;
Number v2 = v1;
System.out.println(v1.hashCode() + " -> " + new KeyWrapper(v1).hashCode());
System.out.println(v2.hashCode() + " -> " + new KeyWrapper(v2).hashCode());
}
private static class KeyWrapper {
private Object obj;
private int hash;
public KeyWrapper(Integer v) {
this.hash = v.hashCode() * 3;
}
public KeyWrapper(Number v) {
this.hash = v.hashCode() * 5;
}
@Override
public int hashCode() {
return hash;
}
}
}
运行此命令时,您将获得以下输出:
12 -> 36
12 -> 60
为什么这是一个坏主意是你不能以保留合同的方式实现equals()
(这是两个相等的对象必须具有相同的哈希码)。在编译时,您可以获得有关如何引用的值的信息,但在运行时您只知道它们的内容。
也就是说,如果你想对实现接口的对象使用不同的哈希码计算,那么你可以编写一个使用KeyWrapper
的{{1}}。
instanceof
当然, 并不关心变量的声明类型,只关心它的实际类型。