2013-11-28 1 views
1

У меня есть несколько вопросов/сомнения для заполнения значения в HashMapКак мы можем получить неизменный объект, если класс не выполнил клонируемые

Я хочу HashMap принять «Студент», как ключ и «Деталь» в качестве значения. Поскольку ключ к HashMap должны быть неизменны у меня есть некоторые сомнения, как это может быть, рассматриваемые если

  1. класс Student не клонируемыми
  2. класс Student имеет отношение к которому в свою очередь, имеют ссылки на «Лаборатория»

    public class Student { 
    
        private String id; 
        private String name; 
        private Department dept; 
    
        public Student(String id, String name, Department dept) 
        { 
         this.id=id; 
         this.name=name; 
         this.dept=dept; 
        }   
    
        public Department getDepartment() 
        { 
         return this.dept; 
        } 
    
    } 
    
    public class Department { 
    
        private String deptId; 
        private Lab lab; 
    
        public Department(String deptId, Lab lab) 
        { 
         this.deptId=deptId; 
         this.lab=lab; 
        } 
    
        public void setLab(Lab lab) 
        { 
         this.lab=lab; 
        } 
    } 
    
    public class Lab { 
    
        private String labId; 
        private String labName; 
    
        public Lab(String labId, String labName) 
        { 
         this.labId=labId; 
         this.labName=labName; 
        } 
    
    } 
    
    public class StudentDetails 
    { 
        private String fatherName; 
        private String address 
    
        public StudentDetails(String fatherName, String address) 
        { 
        this.fatherName=fatherName; 
        this.address=address; 
        } 
    } 
    
    
    public class StudentMaintainer { 
    
        public static void main(String[] args) 
        { 
         StudentDetails stDetails= new StudentDetails("John","Mumbai"); 
         Lab lab= new Lab("100","CS"); 
         Department dept= new Department("900", lab); 
         Student st = new Student("3000",dept); 
    
         Map<Student,StudentDetails> studentMaintainer= new ArrayList<>(); 
         studentMaintainer.put(st,stDetails); 
        } 
    } 
    

Теперь Даже если студент является клонируемым, я могу получить ссылку отдела и вызвать setLab(), который изменяет StudentObject. (Я не прав?)

Теперь, если отдел и лаборатория из сторонних банках, как я могу использовать объект-ученик в моей карте, если hashCode для студентов (primeNumber + Student.id + Department.id + Lab.id). hashcode() [просто какой-то странный случай];

ответ

3

Теперь Даже если студент является клонируемым, я могу получить ссылку отдела и вызвать setLab(), который изменяет StudentObject. (Я не прав?)

Вы правы. Это может произойти и может привести к тому, что ваш класс Student будет мутирован. Для того чтобы экземпляр Student был неизменным, вы не сможете изменить любое из его полей [0]. Это включает вызов чего-то типа метода setter в одном из его полей.

Теперь, если отдел и лаборатория находятся от третьих сторон банки, как я могу использовать Student Object в моей карте, если студент является хэш-код (primeNumber + Student.id + Department.id + Lab.id) .hashcode () [всего лишь странный случай];

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

  • , если третья сторона объекты вы хотите использовать интерфейсы, можно реализовать интерфейс с вашим собственным типом, где тело каждого метода мутаторного бросает исключение (думает, например java.util.Collections.unmodfiableList). У этого есть преимущества, которые вы можете по-прежнему относить к классу сторонних разработчиков в своей кодовой базе, но недостаток, который вызывает методы мутатора, терпит неудачу во время выполнения, а не во время компиляции.
  • адаптеры записи в вашем собственном коде, как это:

    public final class MyImmutableDepartment { 
        private final MyImmutableLab lab; 
        private final String departmentId; 
    
        public MyImmutableDepartment(Department thirdPartyMutableDepartment) { 
         this.departmentId = thirdPartyMutableDepartment.getId(); 
         this.lab = new MyImmutableLab(thirdPartyMutableDepartment.getLab()); 
        } 
    
        // getters and the MyImmutableLab class left as an exercise 
    } 
    

    Это имеет то преимущество, что вы знаете во время компиляции, классы не могут мутировать.

Недостатком обоих подходов является то, что в основном вы должны зеркалировать каждый класс из сторонней библиотеки, чтобы гарантировать, что они неизменяемы.

Я не думаю, что есть другие альтернативы.

[0] Есть некоторые случаи, когда это возможно и может использоваться для внутреннего кэширования, но это достойная рекомендация придерживаться при обучении.

5

Неизменяемость не имеет ничего общего с Cloneable, насколько я понимаю, и на самом деле как раз наоборот. Неизменность имеет больше общего с объявлением окончательного класса и использованием неизменяемых полей, методов, не связанных с перегрузкой, без методов setter, методов getter, которые возвращают глубокие копии полей или неизменяемых полей и т. Д. ... Подробнее об этом читайте A Strategy for Defining Immutable Objects.

Кроме того, ваш код имеет псевдо -constructor:

public void Student(String id, String name, Department dept) 
{ 
    this.id=id; 
    this.name=name; 
    this.dept=dept; 
} 

Истинный конструктор не должен быть объявлен вернуть ничего, даже не недействительными. Лучше бы:

// note the difference? 
public Student(String id, String name, Department dept) 
{ 
    this.id=id; 
    this.name=name; 
    this.dept=dept; 
} 

Кроме того, ваш класс Студент должен правильно переопределить равных и хэш-код, если это хорошо работать в качестве ключа для HashMap.

+0

@ksv Я нарисовал код примера в вашем вопросе для ясности, но здесь вы должны отметить хорошую коррекцию. – Grundlefleck

0

Студенту не обязательно быть неизменным! В частности, требование состоит в том, что поведение equals/hashCode не изменяется, когда ключ находится в HashMap.

Это может быть достигнуто тремя способами:

  • Не осуществлять РАВНО/хэш-код. Если вы используете стандартное равенство по умолчанию, не имеет значения, как вы мутируете ключ. Чтобы прояснить намерение, переопределите эти методы, явным образом вызовите super.equals и сделайте их окончательными.
  • Не включайте поля, которые будут мутировать при вычислении equals/hashCode. Или, если свойства поля могут измениться, но не ссылки, используйте ссылочное равенство (==) вместо field.equals() и вызовите System.identityHashCode (поле) вместо поля.hashCode()
  • Не мутировать объект, пока он используется как ключ в HashMap. Немного опасно, но отлично работает, если ссылки не удерживаются кодом, находящимся вне вашего контроля. Документируйте это требование.

Но в вашем конкретном примере каждый ученик имеет идентификатор. Зачем использовать любые другие свойства при реализации equals/hashCode.?

+0

Объекты, которые используются как ключи в хэшмапах, должны быть неизменными до тех пор, пока они находятся в хэшмапе, но это не означает, что их * типы * должны быть неизменными. Если нет пути выполнения кода, который будет модифицировать объект, пока он является ключом в хэш-карте, тогда объект будет эффективно неизменен в течение этого времени. Во многих случаях такая «эффективная неизменность» может быть применена, если код, который владеет хеш-таблицей, никогда не предоставляет хеш-таблицу и объекты внутри нее ни к чему, что может неправильно их изменить. – supercat

+0

@supercat Разве это не тот пункт 3 в моем ответе? –

+0

Я думаю, что важно различать уровень опасности, создаваемый, надеясь, что код, который может иметь внешние ссылки на изменяемый объект, не будет мутировать его, пока он находится в HashMap, и знает, что * внешние ссылки отсутствуют *. – supercat

Смежные вопросы