Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend immutable types in Java

I have started playing around with immutable value objects in Java while working on a game project, following the "public final fields" approach:

public class Team {
    public final String name, flag;

    public Team(String name, String flag) {
        this.name = name;
        this.flag = flag;
    }
}

This works pretty well for me so far, but I need different sets of extra information about the team in different circumstances. For example, a team has a set color during a match. The question is, what is the best way to deal with these sets of extended information? I know this is a fairly general question, but I want to keep using immutable objects and that might influence the solution.

Here are the options I have come up with. Most of them are probably "good enough", but I'd like to learn some arguments for and against them for future reference.

Option 1: Everything in one class

public class Team {
    public final String name, flag, colorName;
    public final int colorRgb;

    public Team(String name, String flag, String colorName, int colorRgb) {
        this.name = name;
        this.flag = flag;
        this.colorName = colorName;
        this.colorRgb = colorRgb;
    }
}

This takes only one class for all uses, but there is no type-based indication of what extra data is expected/provided.

Option 2: Subclassing

public class TeamWithColor extends Team {
    public final String colorName;
    public final int colorRgb;

    public Team(String name, String flag, String colorName, int colorRgb) {
        super(name, flag);
        this.colorName = colorName;
        this.colorRgb = colorRgb;
    }
}

This might make a content-based equals() implementation impossible.

Option 3: Composition

public class TeamWithColor {
    public final Team team;
    public final String colorName;
    public final int colorRgb;

    public Team(Team team, String colorName, int colorRgb) {
        this.team = team;
        this.colorName = colorName;
        this.colorRgb = colorRgb;
    }
}

Less copying / boilerplate code if the team data and extra data often change independently.

Option 4: Pair/Tuple (using an immutable Pair class)

public class TeamColor {
    public final String colorName;
    public final int colorRgb;

    public Team(String colorName, int colorRgb) {
        this.colorName = colorName;
        this.colorRgb = colorRgb;
    }
}

Pair<Team, TeamColor> teamWithColor = Pair.create(team, teamColor);

... or with a custom class that ties Team and TeamColor together.

I tend toward option 3 or 4, but I'm interested in your opinions, arguments and gut feelings :)

like image 733
Medo42 Avatar asked Aug 04 '12 16:08

Medo42


People also ask

Can we extend immutable class in Java?

In Immutable Objects in Java, the four authors (Haack, Poll, Schafer, and Schubert) include a section on "enforcing immutability." In this section they maintain that classes which extend immutable classes must also themselves be immutable.

Can we modify immutable class in Java?

Immutable class/object is the one whose value cannot be modified. For example, Strings are immutable in Java i.e. once you create a String value in Java you cannot modify it.

Can immutable classes be subclassed?

If you want to enforce immutability, you cannot have subclasses. See for example java. lang. String, which is a final class for this reason: To prevent people from subclassing String to make it mutable.


2 Answers

As you said. The team can appear in different circumstances. These circumstances are the context giving the team the additional attributes.

Therefore I suggest using composition for each different context that add's data.

public class TeamWithColor {
    public final Team team;
    public final TeamColor teamColor;

    public Team(Team team, TeamColor teamColor) {
        this.team = team;
        this.teamColor = teamColor;
    }
}

Maybe you'll have :

public class TeamDuringOlimpics{
    public final Team team;
    public final TeamColor teamColor;
    public final TeamFlag teamFlag;

    public Team(Team team, TeamColor teamColor, TeamFlag teamFlagTeamFlag teamFlag) {
        this.team = team;
        this.teamColor = teamColor;
        this.teamFlag = teamFlag;
    }    
}
like image 197
R-E-L Avatar answered Oct 02 '22 19:10

R-E-L


Composition sounds like a good option for adding contextual data that is required to be mutable.

In Java immutable classes are usually marked final and cannot be extended. See String as an example. That rules out option number 2.

Be weary of using Pairs. There are many good reasons the Pair type has not been added to Java. In this case your data is better modeled by creating a new data type (i.e. thru composition).

Recommended best practices for creating immutable classes: http://www.javapractices.com/topic/TopicAction.do?Id=29

like image 38
Frohnzie Avatar answered Oct 02 '22 18:10

Frohnzie