代码之家  ›  专栏  ›  技术社区  ›  J_Y_C

接口中的ToString()、Equals()和HashCode()。

  •  51
  • J_Y_C  · 技术社区  · 15 年前

    所以,我有一个与需要实现的许多方法的接口,这些方法的名称是无关的。

    实现此接口的对象通常被放入集合中,并且还具有我希望它们使用的特殊toString()格式。

    所以,我认为将hashcode()、equals()和toString()放在接口中比较方便,以确保我记得重写这些函数的默认方法。但是当我将这些方法添加到接口中时,如果我没有实现这三个方法,即使我显式地将它们放在接口中,IDE/编译器也不会抱怨。

    为什么我不能强制执行?如果我不实现任何其他方法,它会抱怨,但它不会强制执行这三种方法。给出了什么?有什么线索吗?

    12 回复  |  直到 8 年前
        1
  •  37
  •   Adam Batkin    15 年前

    Java中的所有对象都继承自 java.lang.Object Object 提供这些方法的默认实现。

    如果您的接口包含其他方法,如果您不通过提供这些方法的实现来完全实现接口,Java会抱怨。但在这种情况下 equals() , hashCode() toString() (以及其他一些您没有提到的)实现已经存在。

    一种方法是在接口中提供不同的方法,比如, toPrettyString() 或者类似的。然后可以调用该方法而不是默认方法 托斯特林() 方法。

        2
  •  39
  •   Stephen C    8 年前

    听起来你很想 类来重写这些方法的默认实现。如果是这样,那么实现这一点的方法就是声明一个抽象超类,该类具有声明为抽象的方法。例如:

    public abstract class MyBaseClass implements ... /* existing interface(s) */ {
    
        public abstract boolean equals(Object other);
    
        public abstract int hashCode();
    
        public abstract String toString();
    }
    

    然后将当前类更改为 extend 这个班。

    这种方法是可行的,但它不是一个理想的解决方案。

    • 对于现有的类层次结构,它可能是有问题的。

    • 这是个坏主意 实现现有接口以扩展特定抽象类的类。例如,可以更改方法签名中的参数以使用抽象类而不是现有接口。但最终结果是代码的灵活性降低了。(而且人们可以找到任何方法来颠覆这一点;例如,通过添加自己的抽象子类来“实现”具有 super.<method>(...) 打电话!)

    • 强加一个特定的类层次结构/实现模式是短视的。您无法预测将来的需求变更是否意味着您的限制会导致困难。(这就是为什么人们建议针对接口而不是特定类进行编程的原因。)


    回到关于接口为什么不强制类重新声明这些方法的实际问题:

    为什么我不能强制执行?如果我不实现任何其他方法,它会抱怨,但它不会强制执行这三种方法。给出了什么?有什么线索吗?

    一个接口强加了一个约束,即实现它的具体类对每个方法都有一个实现。但是,它并不要求 它本身 实现这些方法。方法实现可以从超类继承。在这种情况下,就是这样。继承自的方法 java.lang.Object 保护约束。

    JLS 8.1.5 声明如下:

    “除非所声明的类是抽象的,否则每个直接上级的所有抽象成员方法都必须通过该类中的声明来实现(_§8.4.8.1)。 或者通过从直接超类或直接超接口继承的现有方法声明 ,因为不允许非抽象类具有抽象方法(_§8.1.1.1)。”

        3
  •  17
  •   ChssPly76    15 年前

    所有3种方法都由 java.lang.Object 它由所有其他类(隐式地)扩展;因此存在这些方法的默认实现,编译器没有什么可抱怨的。

        4
  •  7
  •   Steve McLeod    15 年前

    实现接口的任何类也会扩展对象。对象定义hashcode、equals和toString,并具有这三个对象的默认实现。

    你试图达到的目标是好的,但并不可行。

        5
  •  5
  •   Tordek    15 年前

    这些方法的实现来自 Object .

        6
  •  5
  •   Bozho    15 年前

    如果要强制重写equals()和hashcode(),请从抽象超类进行扩展,该类将这些方法定义为抽象的。

        7
  •  4
  •   Matt Ball    15 年前

    您的对象已经包含这三个方法的实现,因为每个对象都从对象继承这些方法,除非它们被重写。

        8
  •  3
  •   Ken Bloom    15 年前

    Java只关心方法是在什么地方定义的。接口并不强制您重新定义新类中第一次从接口继承的方法(如果已经定义了这些方法)。自从 java.lang.Object 已经实现了这些方法,您的新对象符合接口,即使它们不单独重写这三个方法。

        9
  •  3
  •   StriplingWarrior    15 年前

    其他人已经充分回答了你的实际问题。对于特定问题的解决方案,您可以考虑创建自己的方法(可能是GetStringRepresentation、GetCustomHashCode和EqualSobject),并让您的对象扩展一个基类,其equals、ToString和HashCode方法调用这些方法。

    不过,这可能会首先破坏使用接口的目的。这是一些人建议Equals、ToString和HashCode不应该首先包含在对象类中的原因之一。

        10
  •  1
  •   Neel    12 年前

    亚当给了你一个理由,为什么你不能逃避强迫平等,哈希代码和ToString。我将进行以下实现,涉及到Adam和Stephan提供的解决方案:

    public interface Distinct {
        boolean checkEquals(Object other);
        int hash();
    }
    
    public interface Stringable {
        String asString();
    }
    
    public abstract class DistinctStringableObject {
    
        @Override
        public final boolean equals(Object other) {
            return checkEquals();
        }
    
        @Override
        public final int hashCode() {
            return hash();
        }
    
        @Override
        public final String toString() {
            return asString();
        }
    }
    

    现在,任何需要自己明确区分并表示为字符串的类都可以扩展DistinctStringableObject,这将强制实现checkEquals、hashcode和toString。

    样品混凝土等级:

    public abstract class MyDistinctStringableObject extends DistinctStringableObject {
    
        @Override
        public final boolean checkEquals(Object other) {
            ...
        }
    
        @Override
        public final int hash() {
            ...
        }
    
        @Override
        public final String asString() {
            ...
        }
    }
    
        11
  •  0
  •   Philippe Gioseffi    12 年前

    如果您有一个孙子,抽象类将不起作用,因为它的父亲已经重写了equals和hashcode方法,然后您又遇到了问题。

    尝试使用annotatins和apt( http://docs.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html )完成任务。

        12
  •  -2
  •   sazamsk    15 年前

    如果你已经声明了一个接口,默认情况下所有方法都是抽象的,你确实需要提供功能,但是当你在子类中实现它时,你就提供了实现权利。 如您所见,每个类都是一个超类的子类(简单地说,对象就是所有类的超类)。 因此,如果您有一个实现接口的类,那么您需要为这些方法提供实现。但这里有件事需要记住。

    不管怎样,如果您没有在接口中声明这些方法,那么对于首先实现接口的子类,您仍然有这种行为。

    因此,如果不声明它,它仍然存在,另一件事是,由于对象类的这些方法和其他方法对于类的所有对象都存在,所以不需要实现。