小練習:

Измените предыдущий пример HeavenlyBody так, чтобы HeavenlyBody

В классе также есть поле bodyType. В этом поле будет храниться

тип Небесного Тела (например, ЗВЕЗДА, ПЛАНЕТА, ЛУНА и т. д.).

Вы можете включить столько типов, сколько хотите, но они должны поддерживаться на

минимум ПЛАНЕТЫ и ЛУНЫ.

Для каждого из поддерживаемых типов создайте подкласс класса HeavenlyBody.

чтобы ваша программа могла создавать объекты соответствующего типа.

Хотя астрономы могут содрогнуться, наши солнечные системы

разрешить двум телам иметь одно и то же имя, если они не являются

тот же тип тела: так что вы могли бы иметь звезду по имени «БетаМинор» и

например, астероид, также называемый «БетаМинор».

Подсказка: это намного проще реализовать для набора, чем для карты.

потому что карте понадобится ключ, который использует оба поля.

Существует ограничение, что единственные спутники, которые могут быть у планет, должны

быть лунами. Однако даже если вы не реализуете тип STAR, ваша программа

не должны препятствовать тому, чтобы один из них был добавлен в будущем (и спутники STAR

может быть почти любым видом HeavenlyBody).

Тестовые случаи:

1. Планеты и луны, которые мы добавили в предыдущем видео, должны появиться в

коллекции solarSystem и в наборах лун для соответствующих планет.

2. a.equals(b) должен возвращать тот же результат, что и b.equals(a) — equals симметричен.

3. Попытка добавить дубликат в набор не должна приводить к изменению набора (поэтому

исходное значение не заменяется новым).

4. Попытка добавить дубликат на карту приводит к замене оригинала

по новому объекту.

5. В один и тот же набор можно добавить два тела с одинаковым названием, но разными обозначениями.

6. На одну карту можно добавить два тела с одинаковым названием, но разными обозначениями,

и могут быть извлечены из карты.

參考答案:

HeavenlyBody.java

package SetChallenge;
import java.util.HashSet;
import java.util.Set;
public abstract class HeavenlyBody {
    private final Key key;
    private final double orbitalPeriod;
    private final Set<HeavenlyBody> satellites;
    public static Key makeKey(String name, BodyTypes bodyType) {
        return new Key(name, bodyType);
    }
    public HeavenlyBody(String name, double orbitalPeriod, BodyTypes bodyType) {
        this.orbitalPeriod = orbitalPeriod;
        this.satellites = new HashSet<>();
        this.key = new Key(name, bodyType);
    }
    public double getOrbitalPeriod() {
        return orbitalPeriod;
    }
    public Key getKey() {
        return key;
    }
    public boolean addSatellite(HeavenlyBody satellite) {
        return this.satellites.add(satellite);
    }
    public Set<HeavenlyBody> getSatellites() {
        return new HashSet<>(this.satellites);
    }
    @Override
    public final boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof HeavenlyBody) {
            Key objKey = ((HeavenlyBody) obj).getKey();
            return this.key.equals(objKey);
        }
        return false;
    }
    @Override
    public final int hashCode() {
        return this.key.hashCode();
    }
    @Override
    public String toString() {
        return getKey().getName() + ": " + getKey().getBodyType() + ", " + getOrbitalPeriod();
    }

    enum BodyTypes {
        PLANET, DWARF_PLANET, MOON
    }
    public static final class Key {
        private String name;
        private BodyTypes bodyType;
        private Key(String name, BodyTypes bodyType) {
            this.name = name;
            this.bodyType = bodyType;
        }
        public String getName() {
            return name;
        }
        public BodyTypes getBodyType() {
            return bodyType;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if ((obj == null) || (obj.getClass() != this.getClass())) {
                System.out.println("obj is null or not equal!");
                return false;
            }
            String objName = ((HeavenlyBody.Key) obj).getName();
            BodyTypes objBodyType = ((HeavenlyBody.Key) obj).getBodyType();
            return this.name.equals(objName) && this.bodyType.equals(objBodyType);
        }
        @Override
        public int hashCode() {
            return this.name.hashCode() + this.bodyType.hashCode() + 37;
        }
        @Override
        public String toString() {
            return name + ": " + bodyType;
        }
    }
}

class Planet extends HeavenlyBody {
    public Planet(String name, double orbitalPeriod) {
        super(name, orbitalPeriod, BodyTypes.PLANET);
    }
    @Override
    public boolean addSatellite(HeavenlyBody moon) {
        if (moon.getKey().getBodyType() == BodyTypes.MOON) {
            return super.addSatellite(moon);
        }
        return false;
    }
}

class DwarfPlanet extends HeavenlyBody {
    public DwarfPlanet(String name, double orbitalPeriod) {
        super(name, orbitalPeriod, BodyTypes.DWARF_PLANET);
    }
}

class Moon extends HeavenlyBody {
    public Moon(String name, double orbitalPeriod) {
        super(name, orbitalPeriod, BodyTypes.MOON);
    }
}

Данг Нам

Причина в том, что на карте не может быть повторяющихся ключей. Шестое требование этого задания – разрешить добавление тел с одинаковыми именами, но разных типов (типы – планета, луна, карликовая планета и т. п.).

Как вы, возможно, заметили, наша карта содержит только строковые ключи, поэтому попытка сохранить тела с одинаковым именем и разными типами завершится неудачей, поскольку у вас не может быть дубликатов ключей. Я пытаюсь сказать, что, допустим, у вас есть 2 ключа: Плутон (тип Планета) и Плутон (тип DwarfPlanet). Например, если вы написали [map.put("Плутон", new Planet("Плутон", 90.8);] то вы не сможете сделать [map.put("Плутон", new DwarfPlanet("Плутон" , 64.6));] потому что они оба имеют один и тот же строковый ключ «Плутон».

Поэтому вам нужно найти способ обойти это, и он хранит объекты, которые содержат как имя, так и тип в качестве ключей. Объект Key содержит name и bodyType, поэтому мы сможем добавить 2 ключа, которые содержат одно и то же имя (Плутон), но разного типа (один — Planet, а другой — DwarfPlanet). Причиной этого является метод equals() внутри класса Key, метод сравнивает имя 2-х объектов, затем сравнивает bodyType 2-х объектов, и если какие-либо из них отличаются, он возвращает false. Когда вы добавляете ключ на карту, он проверяет, есть ли уже ключ на карте, который вернет true при сравнении с ключом, который вы хотите добавить, но поскольку вы добавляете 2 элемента с тем же именем, но разные bodyType сравнение вернет false, что позволит вам добавить их обоих на карту.
Прошу прощения за многословное и длинное объяснение, надеюсь, оно вам поможет!

Горан

public static final class Key {
        private String name;
        private BodyTypes bodyType;
        private Key(String name, BodyTypes bodyType) {
            this.name = name;
            this.bodyType = bodyType;
        }
        public String getName() {
            return name;
        }
        public BodyTypes getBodyType() {
            return bodyType;
        }
        @Override
        public int hashCode() {
            return this.name.hashCode() + 57 + this.bodyType.hashCode();
        }
        @Override
        public boolean equals(Object obj) {
            Key key = (Key) obj;
            if(this.name.equals(key.getName())) {
                return(this.bodyType == key.getBodyType());
            }
            return false;
        }
        @Override
        public String toString() {
            return this.name + ": " + this.bodyType;
        }
    }

В этом случае ключевой класс имеет 2 поля, имя и тип тела, поэтому, когда 2, хэш-код и равенство зависят от этих 2 полей.

Если вы решите использовать строку, вы можете либо использовать только имя, либо имя + bodyType.toString(), а второй вариант — не очень хорошая идея, так как вам нужно создать еще одну строку и поэкспериментировать с конкатенацией. беспорядок быстро, почему?

Что ж, если позже вы решите, что вам нужно еще одно поле, преобразование всего в строку не рекомендуется. Эту логику проще разделить на классы, подобные этому классу Key, чтобы вы могли решить, какие поля вы хотите использовать для ключей и хеширования.

В oop у вас есть классы, поэтому класс, который, например, используется в качестве ключа, является хорошей практикой, класс, который передает данные с сервера на клиент, является хорошей практикой и т. д.

Методы equals() и hashCode() в Java

本篇使用代碼