0
package main;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public final class Tutor { 

    private final String name;          
    private final Set<Student> tutees;            

    public Tutor(String name, Student[] students) {           
        this.name = name;    
        this.tutees = new HashSet<Student>();    
        for (int i = 0; i < students.length; i++) {     
            tutees.add(students[i]);                  
        }   
    }     

    public Set<Student> getTutees()  { return Collections.unmodifiableSet(tutees); }   

    public String getName()  { return name; } 

}  

Is there more that could be done to make this class immutable? String is already immutable, the set is returned unmodifiable. The tutees and name variables are private and final. What else could be done? If the only classes using the Tutor class were within the package, could I change the constructor, getTutees method and getName method to package-private?

Edit:

Here is the Student class, the question asked me to describe the necessary changes to also make Student immutable. I have commented out both setter methods so I can make the variables final. Is this the only way to make it truly immutable?

public final class Student {   
    private final String name;   
    private final String course;

    public Student(String name, String course) {     
        this.name = name;    
        this.course = course;   
    }     

    public String getName() { return name; }   

    public String getCourse() { return course; }   

    //public void setName(String name) { this.name = name; }   

    //public void setCourse(String course) { this.course = course; }  
}
Anthony
  • 13
  • 5
  • I think you might want this on code review... – Zizouz212 Aug 20 '15 at 02:46
  • If `Student` is not mutable the caller of your code can modify it later. Deep copy the array. – fukanchik Aug 20 '15 at 02:47
  • Modifications to Strings can cause creation of new strings, you can use a final StringBuffer. – Ashutosh Aug 20 '15 at 02:47
  • 1
    Making something package-private has nothing to do with immutability. On that note: a class is either immutable or not -- there's no in-between. If you look at your code, there is no way to change anything in the Tutor object once it has been created. This says nothing about the `Student` object however, which can still be mutated through the `getTutees()` method, if `Student` is mutable. – Jeroen Vannevel Aug 20 '15 at 02:48
  • Make the constructor private and implement singleton pattern. – Ravindra babu Aug 20 '15 at 02:48
  • @Ashutosh `String`s in java are immutable. – fukanchik Aug 20 '15 at 02:48
  • @Ashutosh What are you talking about? Strings are immutable, and the `name` field is immutable because of the `final` keyword, so there's **no way** to change the name. – Andreas Aug 20 '15 at 02:49
  • Thanks for adding `Student` to the question. As given, however, the `tutees` set is an *identity* set, since `Student` doesn't implement `equals()` and `hashCode()`. – Andreas Aug 20 '15 at 03:18
  • @fukanchik You are right, I missed the final keyword before the name. – Ashutosh Aug 20 '15 at 07:15

3 Answers3

3

As a minor optimization, you can make tutees immutable, so it cannot even be changed inside Tutor.

public Tutor(String name, Student[] students) {
    this.name = name;
    Set<Student> tuts = new HashSet<>();
    for (Student student : students) {
        tuts.add(student);
    }
    this.tutees = Collections.unmodifiableSet(tuts);
}
public Set<Student> getTutees() { return this.tutees; }

Shorter version:

public Tutor(String name, Student[] students) {
    this.name = name;
    this.tutees = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(students)));
}
public Set<Student> getTutees() { return this.tutees; }
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • If references to `Student` objects are held elsewhere when this object is constructed and `Student` instances are mutable, then this class is still mutable even as you've written it. Now that the implementation of `Student` can be reviewed, we can confidently say that your class is immutable as written. – scottb Aug 20 '15 at 03:10
  • 1
    @scottb True. There's no way to truely make `Tutor` immutable if Student is mutable. And your comment is true even if `Student` references are not held elsewhere, as the reference can be obtained here. – Andreas Aug 20 '15 at 03:14
2

Your class' immutability depends solely on immutability of Student class. If Student is immutable, then Tutor is immutable and vice versa. Nothing else is necessary to ensure immutability of the Tutor class.

Regarding visibility. If your class is used only within the package, make is package-private (on class level). Leave public methods public.

Aivean
  • 10,692
  • 25
  • 39
  • Immutability is conventionally thought of as an all-or-nothing phenomenon, but there can be shades of gray. If `Student` is *effectively immutable* ... or ... there is an implementation contract that a reference to `Student[]` must not be held after it is added to this class ... then it can be appropriate to regard the class as immutable. This is the case with wrappers such as Collections.unmodifiableList(). – scottb Aug 20 '15 at 03:03
  • @scottb I agree about *effectively immutable* `Student`, don't see how it is different from what I said. But when `Student` is mutable, you can't be sure that it will not be mutated after it is returned by `getTutees` method, as `Student` instances are returned *as is*, without wrapping or copying. – Aivean Aug 20 '15 at 04:02
1

Immutables is a handy toolkit for making immutable objects in Java. If you build your entire domain using it, it will be immutable. It takes the question of 'Is this object immutable' out of the equation.

Alex Taylor
  • 8,343
  • 4
  • 25
  • 40