实现 Java 可比接口?

Implementing the Java comparable interface?

我不确定如何在我的复杂 class 中实现类似的接口。我有以下示例代码,我正在使用它来尝试理解它。我知道它应该是 public double compareTo (Complex o) 之类的东西,但我不太确定该怎么做。关于我将如何实施它有什么建议吗?:

import java.util.Scanner;

public class Complex implements Cloneable, Comparable {
    private double real;
    private double imag;

    /*
     * public Object clone() throws CloneNotSupportedException { Complex
     * objClone = new Complex(); objClone.setReal(this.real);
     * objClone.setImag(this.imag); return objClone; }
     */

    public Complex(double real, double imag) {
        this.real = real;
        this.imag = imag;
    }

    public Complex(double real) {
        this.real = real;
    }

    public Complex() {

    }

    public void setReal(double real) {
        this.real = real;
    }

    public void setImag(double imag) {
        this.imag = imag;
    }

    public double getReal() {
        return real;
    }

    public double getImag() {
        return imag;
    }

    public void add(Complex num1, Complex num2) {
        this.real = num1.real + num2.real;
        this.imag = num1.imag + num2.imag;

    }

    public Complex subtract(Complex num) {
        Complex a = this;
        double real = a.real - num.real;
        double imag = a.imag - num.imag;
        return new Complex(real, imag);
    }

    public Complex multiply(Complex num) {
        Complex a = this;
        double real = a.real * num.real - a.imag * num.imag;
        double imag = a.real * num.imag + a.imag * num.real;
        return new Complex(real, imag);
    }

    public Complex divide(Complex c1, Complex c2) {
        return new Complex((c1.real * c2.real + c1.imag * c2.imag) / (c2.real * c2.real + c2.imag * c2.imag),
                (c1.imag * c2.real - c1.real * c2.imag) / (c2.real * c2.real + c2.imag * c2.imag));
    }

    public double absolute() {
        return Math.sqrt(real * real + imag * imag);
    }

    public String toString() {
        return this.real + " + " + this.imag + "i";
    }

    @Override
    public Complex clone() throws CloneNotSupportedException {
        super.clone();
        return new Complex(real, imag);
    }

    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);
        System.out.print("Enter the first set of complex numbers respectively: ");
        double a = in.nextDouble();
        double b = in.nextDouble();

        Complex c1 = new Complex(a, b);

        System.out.print("Enter the second set of complex numbers respectively: ");
        double c = in.nextDouble();
        double d = in.nextDouble();

        Complex c2 = new Complex(c, d);

        Complex result = new Complex(c, d);
        result.add(c1, c2);

        System.out.println("(" + a + " + " + b + "i) + (" + c + " + " + d + "i) = " + result.toString());
        System.out.println("(" + a + " + " + b + "i) - (" + c + " + " + d + "i) = " + c1.subtract(c2));
        System.out.println("(" + a + " + " + b + "i) * (" + c + " + " + d + "i) = " + c1.multiply(c2));
        System.out.println("(" + a + " + " + b + "i) / (" + c + " + " + d + "i) = " + result.divide(c1, c2).toString());
        System.out.println("|" + a + " + " + b + "i| = " + c1.absolute());

    }

    public double compareTo(Complex other) {

        return this.getReal() - other.getReal();
    }

}

首先,Comparator接口returnsintcompareTo方法,不是double。其次,如果你想在 Comparator 中比较两个双精度值,你不应该使用 a - b 模式。相反,使用预定义的 Double.compare 方法:

public int compareTo(Complex other) {
    return Double.compare(this.getReal(), other.getReal());
}

此方法小心处理所有特殊值,如 -0.0NaN,手动处理起来不是很容易。请注意,其他类型也存在类似的方法:Integer.compareLong.compare 等。最好使用它们。

当然要注意,复数没有自然顺序。这里只比较实部,完全忽略虚部

从数学的角度来看,复数无法排序,因此不适合 Comparable interface. To quote the wikipedia article:

Because complex numbers are naturally thought of as existing on a two-dimensional plane, there is no natural linear ordering on the set of complex numbers.
There is no linear ordering on the complex numbers that is compatible with addition and multiplication. Formally, we say that the complex numbers cannot have the structure of an ordered field. This is because any square in an ordered field is at least 0, but i2 = −1.

话虽如此,从技术上讲,没有什么可以阻止您实现此接口。例如,您可以决定先按实部排序,然后再按虚部排序。请注意,compareTo 方法的契约要求您 return 一个 int,而不是 double。此外,您应该将 class 定义为扩展 Comparable<Complex> 而不是原始的 Comparable,这样您就不必在转换和运行时类型检查方面陷入困境:

@Override
public int compareTo(Complex other) {
    int realCompare = Double.compare(getReal(), other.getReal());
    if (realCompare != 0) {
        return realCompare;
    }
    return = Double.compare(getImag(), other.getImag()); 
}

编辑:
JDK 8 的 Comparator 接口的改进允许更优雅的实现具有相同的行为:

public int compareTo(Complex other) {
    return Comparator.comparingDouble(Complex::getReal)
                     .thenComparingDouble(Complex::getImag)
                     .compare(this, other);
}

有几点值得注意。

  1. 正如其他答案所指出的那样,如果 class 的实例有自然顺序,您通常只应实施 Comparable。由于复数没有自然排序,您可能不应该实现 Comparable

  2. 如果您要提供自然排序,那么您应该实现 Comparable<Complex> 来表示与 Complex 的其他实例进行比较(而不是与其他对象进行比较)。

  3. 实现 Comparable 的更好替代方法是为您的 class 提供一个或多个 Comparator 对象,可用于提供与您一样多的排序想。例如:

    public class Complex {
        private double real;
        private double imaginary;
    
        public static final Comparator<Complex> COMPARE_BY_REAL =
            Comparator.comparingDouble(Complex::getReal);
    
        public static final Comparator<Complex> COMPARE_BY_IMAGINARY =
            Comparator.comparingDouble(Complex::getImaginary);
    
        public static final Comparator<Complex> COMPARE_BY_MODULUS =
            Comparator.comparingDouble(Complex::getModulus);
    
        private double getModulus() {
            return Math.sqrt(real * real + imaginary * imaginary);
        }
     }
    

然后 class 的用户可以选择对使用有意义的顺序:

Optional<Complex> closestToOrigin = complexList.stream().min(Complex::COMPARE_BY_MODULUS);