JavaRush /Java блог /Random /Кофе-брейк #216. В чем различия между equals и compareTo ...

Кофе-брейк #216. В чем различия между equals и compareTo в Java. Шаблон проектирования Visitor

Статья из группы Random

В чем различия между equals и compareTo в Java

Источник: Medium Из этого гайда вы узнаете о различиях между методами equals() и compareTo(), а также посмотрите на примеры их применения в языке Java. Кофе-брейк #216. В чем различия между equals и compareTo в Java. Шаблон проектирования Visitor - 1Java предоставляет разработчикам два метода для сравнения объектов: equals() и compareTo(). Они используются для сравнения объектов, но при этом у них разные цели и реализации.

Equals()

Метод equals() применяют для сравнения объектов на равенство. Он возвращает true, если два объекта равны, и false, если они не равны. По умолчанию метод equals() в Java сравнивает объекты на основе их расположения в памяти. Иными словами, если у двух объектов одинаковый адрес в памяти, они считаются равными друг другу. Однако мы можем переопределить реализацию equals() по умолчанию, чтобы сравнивать объекты на основе их свойств. Давайте рассмотрим простой пример, чтобы увидеть, как это работает.

Пример: реализация equals() с идентификатором

Предположим, у нас есть класс Person, который содержит имя и идентификатор (ID). Мы хотим сравнить два объекта Person по их идентификатору, а не по их расположению в памяти. Вот реализация метода equals(), который сравнивает объекты Person по их идентификатору:

public class Person {
    private String name;
    private int id;

    public Person(String name, int id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Person person = (Person) o;
        return id == person.id;
    }
}
В этой реализации мы сначала проверяем аргумент на соответствие null или не является ли он экземпляром класса Person. Если любое из этих условий true, мы возвращаем false. Если аргумент является экземпляром класса Person, мы приводим его к объекту Person и сравниваем его идентификатор с идентификатором текущего объекта. Если идентификаторы равны, то мы возвращаем true, указывая, что два объекта равны. Обратите внимание, что в этой реализации мы сравниваем только свойство ID. Если у двух объектов Person одинаковый идентификатор, они считаются равными, независимо от их имени.

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person(1, "John");
        Person p2 = new Person(1, "Jane");

        System.out.println(p1.equals(p2)); // true
        System.out.println(p1.hashCode() == p2.hashCode()); // true

        Set<Person> set = new HashSet<>();
        set.add(p1);
        set.add(p2);

        System.out.println(set.size()); // 1
    }
}

//*******************************

true
true
1

Пример: реализация equals() со всеми элементами

Теперь давайте рассмотрим другой пример, в котором мы сравним два объекта на основе всех их свойств. Для этого мы реализуем метод equals() следующим образом:

public class Book {
    private String title;
    private String author;
    private int pages;

    public Book(String title, String author, int pages) {
        this.title = title;
        this.author = author;
        this.pages = pages;
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }

        if (!(o instanceof Book)) {
            return false;
        }

        Book book = (Book) o;
        return Objects.equals(title, book.title) &&
                Objects.equals(author, book.author) &&
                pages == book.pages;
    }
}
В этой реализации мы сначала проверяем, является ли аргумент тем же объектом, что и текущий объект. Если это так, возвращаем true. Если аргумент не является экземпляром класса Book, возвращаем false. Если же аргумент является объектом Book, мы сравниваем все его свойства со свойствами текущего объекта. И если все свойства равны, возвращаем true, указывая, что два объекта равны.

public class Main {
    public static void main(String[] args) {
        Book b1 = new Book(1, "The Great Gatsby");
        Book b2 = new Book(1, "To Kill a Mockingbird");

        System.out.println(b1.equals(b2)); // false
        System.out.println(b1.hashCode() == b2.hashCode()); // false

        Set<Book> set = new HashSet<>();
        set.add(b1);
        set.add(b2);

        System.out.println(set.size()); // 2
    }
}

//**************************************************

false
false
2
Обратите внимание, что мы используем метод Objects.equals() для сравнения свойств title и author, поскольку они являются строками. Мы также используем оператор == для сравнения свойств pages, потому что это тип int.

CompareTo()

Метод compareTo() используется для сравнения объектов на основе их естественного порядка. Он возвращает отрицательное целое число (integer), ноль или положительное целое число в зависимости от того, будет ли текущий объект меньшим, равным или большим, чем объект, переданный в качестве аргумента. Естественный порядок определяется самим классом и должен соответствовать методу equals. В случаях, когда естественный порядок класса не определен четко, например, при сравнении двух объектов BigDecimal, метод compareTo может не соответствовать методу equals. В итоге это может привести в определенных ситуациях к неожиданному поведению. Важно помнить об этом при реализации этих методов, чтобы убедиться, что они согласуются друг с другом и с естественным порядком класса. Теперь давайте посмотрим на пример реализации метода compareTo. Допустим, у нас есть класс с именем Person, в котором есть два поля: имя (name) и возраст (age). Мы хотим сравнить экземпляры этого класса по возрасту. Вот пример реализации метода compareTo:

  Person p1 = new Person("John", 30);
  Person p2 = new Person("Jane", 25);

  System.out.println(p1.compareTo(p2)); // 1
  System.out.println(p2.compareTo(p1)); // -1
  System.out.println(p1.compareTo(p1)); // 0
В этом примере мы используем статический метод Integer.compare для сравнения возраста двух объектов Person. Этот метод возвращает отрицательное целое число (integer), если возраст текущего объекта меньше возраста другого объекта; ноль, если они равны; и положительное целое число, если возраст текущего объекта больше возраста другого объекта.

 Integer a = 10;
 Integer b = 20;

 System.out.println(a.compareTo(b)); // -1
 System.out.println(b.compareTo(a)); // 1
 System.out.println(a.compareTo(a)); // 0
Важно учесть, что при реализации метода compareTo мы должны убедиться, что он совместим с методом equals. Это означает, что если два объекта равны методу equals, то они также должны возвращать ноль при сравнении с использованием метода compareTo. Если это требование не выполнится, в определенных ситуациях может возникнуть неожиданное поведение. Давайте изменим класс Person, чтобы добавить метод equals, который сравнивает поля name и age:

public class Person {
    private String name;
    private int age;

    // constructor, getters and setters

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
В этом примере мы переопределяем метод equals, унаследованный от класса Object, для сравнения полей имени (name) и возраста (age). Мы также переопределяем метод hashCode, чтобы гарантировать, что объекты, равные в соответствии с методом equals, имеют одинаковый хеш-код. Учтите, что методы equals и hashCode всегда следует реализовывать вместе, поскольку они используются во многих коллекциях Java, таких как HashSet и HashMap.

Заключение

Хотя методы equals и compareTo используются для сравнения объектов, они служат разным целям. Метод equals используется для проверки равенства объектов, а метод compareTo используется для упорядочения объектов. При реализации этих методов важно обеспечить их согласованность друг с другом, чтобы избежать неожиданного поведения.

Шаблон проектирования Visitor в Java

Источник: Medium Благодаря этой публикации вы сможете более подробно изучить шаблон проектирования Visitor и ознакомитесь с практическим примером его использования в языке Java. Visitor — это поведенческий паттерн (шаблон) проектирования в Java, который позволяет отделить алгоритмы от объектов, с которыми они работают. Этот шаблон используется, когда вы хотите добавить новые операции в структуру объекта, не изменяя сам объект. Чтобы лучше понять шаблон проектирования Visitor, давайте возьмем пример с набором различных фигур, таких как круги, квадраты и треугольники. Предположим, мы хотим вычислить площадь каждой фигуры, но не хотим изменять сами классы фигур. Для этого мы можем создать интерфейс с именем Shape и определить метод accept, который принимает Visitor в качестве параметра. Мы также создаем конкретные элементы для каждой формы, такие как круг (Circle), квадрат (Square) и треугольник (Triangle), которые реализуют интерфейс Shape.

public interface Shape {
    void accept(ShapeVisitor visitor);
}

public class Circle implements Shape {
    private double radius;
    public Circle(double radius) {
        this.radius = radius;
    }
    public double getRadius() {
        return radius;
    }
    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}

public class Square implements Shape {
    private double side;
    public Square(double side) {
        this.side = side;
    }
    public double getSide() {
        return side;
    }
    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}

public class Triangle implements Shape {
    private double base;
    private double height;
    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }
    public double getBase() {
        return base;
    }
    public double getHeight() {
        return height;
    }
    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}
Теперь мы создаем интерфейс Visitor под названием ShapeVisitor и определяем методы для каждого типа формы (shape). Например, мы можем определить метод visitCircle для вычисления площади круга (circle).

public interface ShapeVisitor {
    void visit(Circle circle);
    void visit(Square square);
    void visit(Triangle triangle);
}

public class AreaCalculator implements ShapeVisitor {
    private double totalArea = 0;
    @Override
    public void visit(Circle circle) {
        totalArea += Math.PI * circle.getRadius() * circle.getRadius();
    }
    @Override
    public void visit(Square square) {
        totalArea += square.getSide() * square.getSide();
    }
    @Override
    public void visit(Triangle triangle) {
        totalArea += 0.5 * triangle.getBase() * triangle.getHeight();
    }
    public double getTotalArea() {
        return totalArea;
    }
}
Наконец, мы создаем структуру объекта, которая содержит различные формы (shapes), добавляя их в список. Затем мы вызываем метод accept для каждой формы, передавая AreaCalculator в Visitor.

public class ObjectStructure {
    private List<Shape> shapes = new ArrayList<>();
    public void addShape(Shape shape) {
        shapes.add(shape);
    }
    public void calculateArea(AreaCalculator areaCalculator) {
        for (Shape shape : shapes) {
            shape.accept(areaCalculator);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.addShape(new Circle(5));
        objectStructure.addShape(new Square(4));
        objectStructure.addShape(new Triangle(3, 6));
        AreaCalculator areaCalculator = new AreaCalculator();
        objectStructure.calculateArea(areaCalculator);
        System.out.println("Total area: " + areaCalculator.getTotalArea());
    }
}

Заключение

Шаблон проектирования Visitor — это мощный инструмент для отделения алгоритмов от объектов, с которыми они работают. Используя этот шаблон, вы можете создавать более гибкий и удобный для сопровождения код, который легче модифицировать и расширять. С помощью представленного здесь примера теперь вы можете применять шаблон Visitor в своих проектах для улучшения дизайна программного обеспечения и создания более эффективных решений.
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ