1

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

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

4

3 回答 3

4

您不能这样做,因为 Java 不支持非多态实例方法(静态方法不是多态的,如上一个答案所示)。你可以做的是让你的类直接实现Bar,而是另一个BarProvider带有toBar()orgetBar()方法的接口(例如),它返回一个自定义类型的对象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对象存储为最终字段(以避免多次初始化),并具有允许您BarBarProvider.


另一种可能性是使用外部提供者,这使您的计算

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在覆盖方法上使用修饰符)

于 2013-08-27T19:30:39.267 回答
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

重申一下:这不是一个好主意。我只是为了教育目的让你知道。

于 2013-08-27T19:26:39.797 回答
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使用instanceof.

public class Example {

    public static void main(String[] argv) throws Exception {
        Integer v1 = 12;
        String v2 = "foo";

        System.out.println(v1.hashCode() + " -> " + new KeyWrapper(v1).hashCode());
        System.out.println(v2.hashCode() + " -> " + new KeyWrapper(v2).hashCode());
    }

    private static class KeyWrapper {
        private Object wrapped;

        public KeyWrapper(Object obj) {
            this.wrapped = obj;
        }

        @Override
        public boolean equals(Object obj) {
            return wrapped.equals(obj);
        }

        @Override
        public int hashCode() {
            return (wrapped instanceof Number) ? wrapped.hashCode() * 3 : wrapped.hashCode() * 5;
        }   
    }
}

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

于 2013-08-27T19:58:44.377 回答