Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to instantiate immutable classes in a bidirectional association?

I have two immutable classes: User and Department, they are connected using a bidirectional association - User has a reference to Department and Department has a list of Users. How to create a new Department instance with the provided Users?

Code:

class User {
    private final Department department;
    private final String name;

    public User(Department department, String name) {
        this.department = department;
        this.name = name;
    }
}

class Department {
    private final List<User> users;
    private final String name;

    public Department(List<User> users, String name) {
        this.users = new ArrayList<>(users);
        this.name = name;
    }
}
like image 567
Adam Siemion Avatar asked Apr 08 '15 11:04

Adam Siemion


People also ask

Can we do instantiation for an immutable class in Java?

Immutable class, once an object is instantiated, we cannot modify its content. In Java, all the wrapper classes (let's say, Integer, Short, Boolean, Byte, etc.) and String, etc. classes — are immutable.


2 Answers

I feel in you case you can slightly modify your design and use special UsersBuilder, i.e.

class Department {
    final List<User> users;
    final String name;

    public Department(String name) {
        this.users = UsersBuilder.buildUsers(this);
        this.name = name;
    }
}


class UsersBuilder {

    public static List<User> buildUsers(Department department) {
        List<User> usersList = new ArrayList<>();

        // add users to the list via department reference

        return Collections.unmodifiableList(usersList);
    }    
}

In general, it is not really good idea to use object's reference before its constructor finishes; but in this particular case it looks safe.

In this case these objects will be really immutable.

like image 112
Andremoniy Avatar answered Oct 05 '22 00:10

Andremoniy


You can produce immutable Departments and Users with an extra constructor on Department. From the questions' code, it is inferred that

  • A User object is just an association between a String and a Department
  • User references can't exist without a Department reference.

Since Users are truly just Strings associated to a Department, a Department can be constructed with a List<String> that represents all User names to be included and use that List<String> to create a List<User> within the Department constructor.

Note: what @andremoniy said about letting this escape from a constructor should not be made a habit of, but it is safe in this case since it is only being passed to a User instance's constructor where that User instance can't be accessed before the Department constructor returns.

Here's what it would look like, in Java 8:

public final class User {
    private final Department department;
    private final String name;

    public User(Department department, String name) {
        this.department = department;
        this.name = name;
    }

    public Department getDepartment() {
        return department;
    }

    public String getName() {
        return name;
    }
}

public final class Department {
    private final List<User> users;
    private final String name;

    ///Reversed argument list to avoid collision after erasure
    public Department(String name, List<String> users) {
        this.users = Collections.unmodifiableList(users.stream()
                .map((s) -> new User(this,s)).collect(Collectors.toList()));
        this.name = name;
    }

    public Department(List<User> users, String name) {
        this.users = Collections.unmodifiableList(users);
        this.name = name;
    }

    public List<User> getUsers() {
        return users;
    }

    public String getName() {
        return name;
    }
}

One issue this solution has is that once a Department instance is created, it can be added to new instances of User without the constraint that a new instance of Department be created with an updated List. Consider other abstractions or creational patterns (a full blown Builder implementation where all constructors are private would be a good match here) if you need to support the addition/deletion of users from a Department while maintaining immutability.

like image 29
whaley Avatar answered Oct 05 '22 00:10

whaley