Java中是否有一种方法可以为对象的接口表示调用不同的hashCode()

时间:2013-08-27 19:21:15

标签: java interface

我一直在处理域问题,其中我有实现公共接口的类,我希望能够以不同方式从这些对象中获取哈希值,具体取决于对象是作为接口访问还是作为具体类实例访问。基本上我想要的是以下内容:

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的基本实现;

3 个答案:

答案 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

当然, 并不关心变量的声明类型,只关心它的实际类型。