有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

如何在JAVA中使用带有compareTo的双分派?

Java初学者。。。我对如何在JAVA中将双重分派(和访问者模式)与compareTo方法一起应用感到有点困惑

示例场景:
假设我有一个Animal接口,我将在其中实现4个类:狗、猫、老鼠和兔子
我想确定,如果我对这些动物的列表进行排序,鼠标<;小兔子<;猫<;狗
想知道我将如何实现它
现在我能想到的最好办法就是在比较中加入一些if-elseif。。。但这显然不是派遣。。。 谢谢

Animal接口

package animals;

public interface Animal extends Comparable<Animal> {
  void makeSound();
}

Dog

package animals;

public class Dog implements Animal {
  @Override public void makeSound() {
    System.out.println("wow");
  }

  @Override public int compareTo(Animal o) {
    return 0; // ???? if (o instanceof Cat) {return 1}
  }
}


共 (2) 个答案

  1. # 1 楼答案

    我担心visitor模式在这里不适用,因为它需要两个接口(父类型/类):visitor和Element。但您只有一个父类型

    在您的例子中,您只需创建一个整数属性size,并将其用于比较

  2. # 2 楼答案

    这个Answer by Barskov是正确和明智的。对于您的场景来说,最好的解决方案可能是简单地向四个类中的每个类添加一个最终的静态relativeSize字段

    为了好玩,让我们看看另一种使用Java特性的方法

    密封类

    新的sealed classes特性是Java 16中的previewed,这使得这个过程更简单

    当所有允许的具体类型在编译时都已知时,可以将该事实告知编译器。将类或接口标记为sealed。用实现/扩展类的名称添加permits。然后,编译器执行您的声明,验证所有指定类和指定类是否扩展/实现了密封类/接口

    我们也使用default methods。现代Java允许接口包含代码,如果实现类缺少default方法,则可以运行该方法

    下面是一些示例代码

    注意关键词:sealedpermitsdefault

    package work.basil.example.animalkingdom;
    
    import java.util.List;
    
    public sealed interface Animal                    // 🡄 sealed
            extends Comparable < Animal >
            permits Mouse, Bunny, Cat, Dog            // 🡄 permits
    {
        void makeSound ( );
    
        @Override
        default public int compareTo ( Animal o )     // 🡄 default
        {
            List < Class > animalClasses = List.of( Mouse.class , Bunny.class , Cat.class , Dog.class );
            return
                    Integer.compare
                            (
                                    animalClasses.indexOf( this.getClass() ) ,
                                    animalClasses.indexOf( o.getClass() )
                            )
                    ;
        }
    }
    

    具体的类如下所示。我们在这里使用record,其中编译器隐式地创建构造函数、getter、equals&hashCodetoString。你也可以使用传统的课程

    package work.basil.example.animalkingdom;
    
    final public record Mouse(String name) implements Animal
    {
        @Override
        public void makeSound ( )
        {
            System.out.println( "Squeak." );
        }
    }
    

    看看它的实际效果

    package work.basil.example.animalkingdom;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    public class App
    {
        public static void main ( String[] args )
        {
            List < Animal > animals =
                    new ArrayList <>(
                            List.of(
                                    new Mouse( "Alice" ) ,
                                    new Bunny( "Bob" ) ,
                                    new Cat( "Carol" ) ,
                                    new Dog( "David" ) ,
                                    new Mouse( "Erin" ) ,
                                    new Bunny( "Frank" ) ,
                                    new Cat( "Grace" ) ,
                                    new Dog( "Heidi" )
                            )
                    );
            System.out.println( "animals = " + animals );
    
            Collections.sort( animals );
            System.out.println( "animals = " + animals );
        }
    }
    

    跑步的时候

    animals = [Mouse[name=Alice], Bunny[name=Bob], Cat[name=Carol], Dog[name=David], Mouse[name=Erin], Bunny[name=Frank], Cat[name=Grace], Dog[name=Heidi]]
    animals = [Mouse[name=Alice], Mouse[name=Erin], Bunny[name=Bob], Bunny[name=Frank], Cat[name=Carol], Cat[name=Grace], Dog[name=David], Dog[name=Heidi]]
    

    不要仅仅使用内置的类比较compareTo

    上面的代码很有效,而且很有趣。但在实际工作中,您不应该这样做,也不应该构建这样一个compareTo实现,它只查看类

    阅读^{}Javadoc。您将看到关于compareTo方法应如何与equals保持一致的讨论

    不考虑两个{{CD17>}实例是相等的,因为它们都是同一个类。您还可以比较它们的内容和包含的数据的状态。在record的情况下,equals(和hashCode)的默认实现检查每个成员字段。在传统类中,您可以实现类似的功能,检查id字段或name字段或类似的字段

    如果您确实希望仅按类进行比较,请使用外部^{},而不是在内部实现^{}。比如:

    package work.basil.example.animalkingdom;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    public class App
    {
        public static void main ( String[] args )
        {
            List < Animal > animals =
                    new ArrayList <>(
                            List.of(
                                    new Mouse( "Alice" ) ,
                                    new Bunny( "Bob" ) ,
                                    new Cat( "Carol" ) ,
                                    new Dog( "David" ) ,
                                    new Mouse( "Erin" ) ,
                                    new Bunny( "Frank" ) ,
                                    new Cat( "Grace" ) ,
                                    new Dog( "Heidi" )
                            )
                    );
            System.out.println( "animals = " + animals );
    
            Collections.sort(
                    animals ,
                    new Comparator < Animal >()
                    {
                        @Override
                        public int compare ( Animal o1 , Animal o2 )
                        {
                            List < Class > animalClasses = List.of( Mouse.class , Bunny.class , Cat.class , Dog.class );
                            return
                                    Integer.compare
                                            (
                                                    animalClasses.indexOf( o1.getClass() ) ,
                                                    animalClasses.indexOf( o2.getClass() )
                                            )
                                    ;
                        }
                    } );
            System.out.println( "animals = " + animals );
        }
    }
    

    或者使用更短的lambda语法

            Collections.sort(
                    animals ,
                    ( o1 , o2 ) -> {
                        List < Class > animalClasses = List.of( Mouse.class , Bunny.class , Cat.class , Dog.class );
                        return
                                Integer.compare
                                        (
                                                animalClasses.indexOf( o1.getClass() ) ,
                                                animalClasses.indexOf( o2.getClass() )
                                        )
                                ;
                    } );