I want to remove the duplicates of userNames in the ArrayList. I've tried to convert the ArrayList to an HashSet, but for some reason it doesn't work. The reason why I chose to convert the ArrayList into a HashSet is because it does not allow duplicated values, however, when I use it on my code, it only changes the order in the list:
My code output:
Choreography - Imran Sullivan
Goodfella - Khalil Person
DarknianIdeal - Sophia Jeffery
DarknianIdeal - Clyde Calderon
Frolicwas - Taylor Vargas
Reliable - Aryan Hess
DarknianIdeal - Liyah Navarro
Deservedness - Eadie Jefferson
Reliable - Angel Whitehouse
Choreography - Priya Oliver
How the output should be:
Choreography - Imran Sullivan
Goodfella - Khalil Person
DarknianIdeal - Sophia Jeffery
Frolicwas - Taylor Vargas
Reliable - Aryan Hess
Deservedness - Eadie Jefferson
This is the code. I've splitted the data into an Array so I can print out the data individually.
import java.util.*;
import java.io.*;
public class Task1 {
public static void main(String[] args) {
List<Person> personFile = new ArrayList<>();
Set<Person> splitUserNameList = new HashSet<>(personFile);
try {
BufferedReader br = new BufferedReader(new FileReader("person-data.txt"));
String fileRead = br.readLine();
while (fileRead != null) {
String[] personData = fileRead.split(":");
String personName = personData[0];
String userNameGenerator = personData[1];
String[] userNameSplit = userNameGenerator.split(",");
String newUserNameSplit = userNameSplit[0];
Person personObj = new Person(personName, newUserNameSplit);
splitUserNameList.add(personObj);
fileRead = br.readLine();
}
br.close();
}
catch (FileNotFoundException ex) {
System.out.println("File not found!");
}
catch (IOException ex) {
System.out.println("An error has occured: " + ex.getMessage());
}
for (Person userNames: splitUserNameList) {
System.out.println(userNames);
}
}
}
/* Person Class */
public class Person {
private String personName;
private String userNameGenerator;
public Person(String personName, String userNameGenerator) {
this.personName = personName;
this.userNameGenerator = userNameGenerator;
}
public String getPersonName() {
return personName;
}
public String getUserNameGenerator() {
return userNameGenerator;
}
#Override
public String toString() {
return userNameGenerator + " - " + personName;
}
}
You need to override the equals and hashCode methods on your Person object in order for Set to know which objects are considered the same.
It seems you want any two people with the same userNameGenerator field to be considered equal. In that case, the following will suit your needs:
public class Person {
private String commonName;
private String userNameGenerator;
...
#Override
public int hashCode()
{
return userNameGenerator.hashCode();
}
#Override
public boolean equals(Object o)
{
if (this == o) return true;
if (this.getClass() != o.getClass()) return false;
final Person that = (Person) o;
return this.userNameGenerator.equals(that.userNameGenerator);
}
}
Some important things to note:
In the case of duplicates, the Set will only allow in the first one
so insertion order becomes important.
Sets should not contain mutable elements because mutability destroys
their internal consistency. Your Person class is immutable (has no
setters) which is good but you may want to enforce this immutability even
further by declaring all of it's fields final.
Related
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 11 months ago.
I'm working on a project for a Java class, and I can't seem to get past this NullPointerException. The project is a command-line LinkedIn program. One of the aspects I'm implementing is the ability to add a skillset to a user's profile.
I have a LinkedInUser class in which I define a TreeSet to hold these skillsets in the form of Strings entered by the user. I'm using TreeSet, because the assignment requires them to be sorted.
I define the TreeSet in the LinkedInUser class here:
private Set<String> skillsets = new TreeSet<>();
The action the user takes is defined in the AddSkillsetAction class:
String skillset;
System.out.println("Enter a skillset to add to your list:");
skillset = scanner.nextLine();
loggedInUser.addSkillset(skillset);
System.out.println(skillset + " has been added to your skillsets.");
And the String they enter is passed to the addSkillSet function in the LinkedInUser class:
public void addSkillset(String skillset) {
skillsets.add(skillset);
}
I keep getting a NullPointerException on the line:
skillsets.add(skillset);
What am I doing wrong? I've tested every level up to that line. I even tested the TreeSet inside the addSkillset function with this code:
if(skillsets == null) {
System.out.println("The TreeSet is null.")
}
It's telling me the TreeSet is null. I thought instantiating the Set with:
private Set<String> skillsets = new TreeSet<>();
would actually create an empty TreeSet, instead of it pointing to a null location. Why is my set "skillsets" still pointing to null? What am I doing wrong here?
EDIT:
Here are the full classes:
package edu.institution.asn2;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class LinkedInUser extends UserAccount implements Comparable<LinkedInUser>, Serializable {
private static final long serialVersionUID = 75648957489235739L;
private String type;
private List<LinkedInUser> connections = new ArrayList<>();
private Set<String> skillsets = new TreeSet<>();
public LinkedInUser(String username, String password) {
super(username, password);
}
#Override
public void setType(String type) {
this.type = type;
}
public String getType() {
return this.type;
}
// Add a connection to user's list
public void addConnection(LinkedInUser user) throws LinkedInException {
int index = connections.indexOf(user);
if (index >= 0) {
throw new LinkedInException("You are already connected with this user.");
}
else {
connections.add(user);
}
}
// Remove a connection from the user's connection list
public void removeConnection(LinkedInUser user) throws LinkedInException {
int index = connections.indexOf(user);
if (index < 0) {
throw new LinkedInException("You are NOT connected to this user.");
}
else {
connections.remove(index);
}
}
// Return a copy of the ArrayList of connections
public List<LinkedInUser> getConnections() {
ArrayList<LinkedInUser> copy = new ArrayList<>(connections);
return copy;
}
// Return the number of connections
public int getNumberOfConnections() {
return connections.size();
}
// Return the skillsets
public Set<String> getSkillsets(){
return skillsets;
}
// Add a skillset
public void addSkillset(String skillset) {
skillsets.add(skillset);
}
// Remove a skillset
public void removeSkillset (String skillset) {
if(skillsets.contains(skillset)){
skillsets.remove(skillset);
} else {
System.out.println(skillset + " is not in your skills list.");
}
}
// Override the compareTo function
#Override
public int compareTo(LinkedInUser user) {
int i = this.getUsername().compareToIgnoreCase(user.getUsername());
return i;
}
}
And the class to add a skillset:
package edu.institution.actions.asn7;
import java.util.Scanner;
import edu.institution.ApplicationHelper;
import edu.institution.UserRepository;
import edu.institution.actions.MenuAction;
import edu.institution.asn2.LinkedInUser;
public class AddSkillsetAction implements MenuAction {
#Override
public boolean process(Scanner scanner, UserRepository userRepository, LinkedInUser loggedInUser) {
String skillset;
System.out.println("Enter a skillset to add to your list:");
skillset = scanner.nextLine();
loggedInUser.addSkillset(skillset);
System.out.println(skillset + " has been added to your skillsets.");
ApplicationHelper.incrementSkillsetCount(skillset);
return true;
}
}
After I run and try to add a skillset, I get this error:
Exception in thread "main" java.lang.NullPointerException
at edu.institution.asn2.LinkedInUser.addSkillset(LinkedInUser.java:69)
at edu.institution.actions.asn7.AddSkillsetAction.process(AddSkillsetAction.java:19)
at edu.institution.ApplicationController.process(ApplicationController.java:61)
at edu.institution.LinkedInCLI.main(LinkedInCLI.java:39)
LinkedInUser.java:69 is:
skillsets.add(skillset);
By the way… Your naming is confusing. String skillset; should be String skill, and .addSkill not .addSkillset, because you are adding individual skills rather than adding a set.
Clarifying your naming may clarify your code. Notice the singular skill and plural skills naming used in code below.
You did not provide enough details to diagnose the problem. But I can show you some example code based on your descriptions.
Your problem may be related to your not properly instantiating the TreeSet. Notice in this code that you have a choice of at least two places in which to instantiate:
On the declaration line of skills.
In the constructor. (Code currently commented-out.)
The LinkedInUser class.
package work.basil.linkedin;
import java.util.NavigableSet;
import java.util.Set;
import java.util.TreeSet;
public class LinkedInUser
{
private String name;
private NavigableSet < String > skills = new TreeSet <>();
// Constructor
public LinkedInUser ( final String name )
{
this.name = name;
// this.skills = new TreeSet <>() ;
}
// Modifiers
public void setName ( String name ) { this.name = name; }
public void addSkill ( String skill ) { this.skills.add( skill ); }
// Getters
public String getName ( ) { return name; }
public Set < String > getSkills ( ) { return Set.copyOf( this.skills ); } // Return a unmodifiable copy of the set. (defensive programming)
}
For defensive programming, we return a copy of the set. This unmodifiable copy returned by Set.copyOf has no order. In some implementations, the order may even change arbitrarily for each iterator. If you want to return an ordered NavigableSet instead, do this:
Change the return type of the method to NavigableSet.
Change the code to pass the instance’s set to the constructor of another set.
public NavigableSet < String > getSkills ( ) { return new TreeSet <>(this.skills ); }
Usage.
LinkedInUser alice = new LinkedInUser( "Alice" );
LinkedInUser bob = new LinkedInUser( "Bob" );
alice.addSkill( "Yodeling" );
alice.addSkill( "Tap Dancing" );
bob.addSkill( "Juggling" );
System.out.println( alice.getName() + " does " + alice.getSkills() );
System.out.println( bob.getName() + " does " + bob.getSkills() );
System.out.println( List.of( alice , bob ) );
When run.
Alice does [Yodeling, Tap Dancing]
Bob does [Juggling]
[LinkedInUser{name='Alice', skills=[Tap Dancing, Yodeling]}, LinkedInUser{name='Bob', skills=[Juggling]}]
You said:
I thought instantiating the Set with:
private Set<String> skillsets = new TreeSet<>();
Yes, that would indeed instantiate a TreeSet object, and store a reference to that set in a variable named skillsets. I expect you are placing that code in the wrong location. Again, look at the two locations I suggested earlier in this Answer: on declaration line, or in constructor.
Im trying to make a program that allows the client to input a String. The string length should have 3 characters only and should contain the letters .
My program have to pass through this table and check what this string refers to..
Let's say the client passed this String "AUG", my program should show the name of this String which is "Met".
I made a code, and it worked but it has more then 15 if else-if condition.
My question is : Is there any other way to do it without using if else-if (or switch).
And does polymorphism work in this case ?
Have a look at HashMap
You can build your table with:
Map<String, String> table = new HashMap<>();
table.put("AUG", "Met");
table.put(...);
Then access your table using the user's input:
if(table.containsKey(input)){
return table.get(input);
}
I think I'd go about it with an enum personally (provided performance wasn't a significant concern):
public enum Abbreviations {
Ala("GCU", "GCC", "GCA", "GCG"),
Arg("CGU", "CGC", "CGA", "CGG", "AGA", "AGG")
// ...
;
private final List<String> codons;
private Abbreviations(final String... codons) {
this.codons = Arrays.asList(codons);
}
public boolean contains(final String codon) {
return this.codons.contains(codon);
}
}
And then you can find their matching from the String using something like:
public String find(final String codon) {
for (final Abbreviations abb : Abbreviations.values()) {
if (abb.contains(codon)) {
return abb.name();
}
}
throw new IllegalArgumentException("Unknown codon: '" + codon + "'");
}
You could try an Object Oriented Aproach:
//This is your representation of Codon
//Which has a name e.g. Alanine and an Abreviation object.
public class Codon {
private String name;
private Abreviation abreviation;
public Codon(String name, Abreviation abreviation) {
this.name = name;
this.abreviation = abreviation;
this.abreviation.addCodon(this);
}
#Override
public String toString() {
return "Codon [name=" + name + ", abreviation=" + abreviation + "]";
}
}
import java.util.ArrayList;
import java.util.List;
// This is a representation of an abreviation object
// Which has an abreviation: ALA;
// and the name of the abreviation "Alanine".
public class Abreviation {
private String abreviation;
private String name;
private List<Codon> codons = new ArrayList<>();
public Abreviation(String abreviation, String name) {
super();
this.abreviation = abreviation;
this.name = name;
}
public boolean addCodon(Codon codon) {
return this.codons.add(codon);
}
#Override
public String toString() {
return "Abreviation [abreviation=" + abreviation + ", name=" + name + "]";
}
}
// Here is your program, where it's being build all the Codons structure with your respective Abbreviation.
public class App {
public static void main(String[] args) {
// This is abreviation, it'll will associated with the codon
Abreviation alanine = new Abreviation("Ala", "Alanine");
// Here it's being build the codon CGU, which has abreviation alanine
Codon GCU = new Codon("GCU", alanine);
// Then using toString method it prints what have been done
System.out.println(GCU);
}
}
You can put all of your codons into a List, so you can search and retrieve then.
I have a list of objects "SaleItem". they are all objects of the same class. each object has a String field "name" and an int field "value". I want to see if one of the objects contains a name. It seems that I can't use the "contains" method to do this. I see two solutions. one is to iterate through all the objects to check if one has said name:
for (SaleItem item: myList) {
if (item.getName() == "banana") {
// do stuff
}
}
The other solution would be to create a new list of Strings from "myList" and use the contains method on that:
ArrayList<String> nameList = new ArrayList<>();
for (SaleItem item: myList) {
nameList.add(item.getName());
}
if (nameList.contains("banana")) {
// do stuff
}
I imagine the first method would be most efficient if I'm only doing it once, and the second would be more efficient if I'm doing it many times. Being a bit of a newbie without a formal education, I don't know what's proper in this situation.
Since SaleItem.getName() returns a string, you should be able to use "contains" method.
It seems like you have initialized the ArrayList or the SaleItem object incorrectly.
public class TestApp {
public static void main(String[] args) {
List<SaleItem> list = new ArrayList<SaleItem>();
SaleItem s1 = new SaleItem();
s1.setName("banana");
s1.setValue(1);
SaleItem s2 = new SaleItem();
s2.setName("apple");
s2.setValue(2);
list.add(s1);
list.add(s2);
for (SaleItem item: list) {
if (item.getName().contains("banana")) {
System.out.println("Pass");
}
}
}
}
class SaleItem {
private String name;
private int value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
Try with this code
public class SaleItem {
private String itemName;
public String getItemName() {
return itemName;
}
public SaleItem setItemName(String itemName) {
this.itemName = itemName;
return this;
}
#Override
public String toString() {
return "[SaleItem : { itemName = " + this.getItemName() + " }]";
}
public static void main(String[] args) {
ArrayList<SaleItem> nameList = new ArrayList<>();
nameList.add(new SaleItem().setItemName("banana"));
nameList.add(new SaleItem().setItemName("grape"));
nameList.add(new SaleItem().setItemName("watermelon"));
nameList.add(new SaleItem().setItemName("orange"));
nameList.add(new SaleItem().setItemName("guava"));
for (SaleItem item : nameList) {
if (item.toString().contains("banana")) {
// Do this
}
}
}
}
A List's .contains method isn't magical, it will generally just loop through the elements checking for equality, O(n) linear performance.
Your first solution is probably fine.
If you really did expect repeated access and wanted better than linear performance on subsequent lookups, you'd probably want to construct a Map<String,SaleItem>, or a Set<String> depending on what you wanted to do with it. But those solutions would normally only work on exact matches. Once you need case-insensitive matches, they have to be TreeMap or TreeSet with a case-insensitive comparator. And if you want partial matching (like using String.contains() or a regular expression), you'd want to go back to a linear search.
But don't do any of that unless you have to. Keep it simple.
So I have this class "Member" :
package pkgData;
import java.io.Serializable;
public class Member implements Comparable<Member>, Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private String city;
public Member(String nameOfMember,String location) {
super();
this.name = nameOfMember;
this.city=location;
}
public String getNameOfMember() {
return name;
}
public String getLocationOfMember() {
return city;
}
public void setNameOfMember(String nameOfMember) {
this.name = nameOfMember;
}
#Override
public String toString() {
return name +", " + city;
}
#Override
public int compareTo(Member o) {
int result =this.getNameOfMember().compareTo(o.getNameOfMember());
if(result==0){
result = this.getLocationOfMember().compareTo(o.getLocationOfMember());
}
return result;
}
}
And I have a JComboBox which is EDITABLE and the model of the ComboBox is DefaultComboBoxModel.
So the problem is that if I cast the selectedItem:
Member nameOfMember = (Member)memberModel.getSelectedItem();
if(nameOfMember== null)
throw new Exception("please select a name and a location");
It only checks if the entered string is empty. If I enter a string like "Name, Location" I always get the exception that String cannot be cast to Member. Which String to I have to enter that the String can be cast to Member?
Here is my JComboBox:
private JComboBox<Member> getComboBoxMember() {
if (comboBoxMember == null) {
comboBoxMember = new JComboBox<Member>();
comboBoxMember.setEditable(true);
comboBoxMember.setModel(memberModel);
}
return comboBoxMember;
}
and here the global variables:
private DefaultComboBoxModel<Member> memberModel;
private JComboBox<Member> comboBoxMember;
String nameOfMember = (String) memberModel
.getSelectedItem();if(nameOfMember==null)throw new Exception("please select a name and a location");else
{
String[] parts = nameOfMember.split(",");
String part1 = parts[0]; // name
String part2 = parts[1]; // location
Member member=new Member(part1, part2);
}
String split & cast method
What you can do is first of all test if the string you get is null, or if it matches well you format. Then, you can create a new object with these elements.
Here's a small example code :
String memberData = (String)memberModel.getSelectedItem();
if(memberData == null || memberData.split(", ")[0].isEmpty() || memberData.split(", ")[1].isEmpty()) {
throw new Exception("Data is incorrect, please provide name and location separated with ", ");
}
Member member = new Member(memberData.split(", ")[0], memberData.split(", ")[1]);
JComboBox method
With Java 7 happened a new possibility of extension to JComboBox, which can now be generically parameterized (as for ArrayLists) in the form JComboBox<Type>. Thus, the objects you can get with getSelectedItem() can now be casted to the generic type you gave in parameter to JComboBox. The only problem is that, when a JComboBox is edited, as in your case, the data is casted to a simple String.
What you can do in your listener method (I will use ActionListener) is the following :
class ItemAction implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
try {
//In case the user has not modified the object
Member member = (Member)box.getSelectedItem();
//Just an example here
if(member != null) {
System.out.println(member.toString());
}
} catch(ClassCastException ex) {
//In case the object has been modified
String data = (String)box.getSelectedItem();
//Apply first method here
}
}
}
But the problem with this method is that you end up using the first method still.
Immutable classes are great but there is one big problem i cant think of a sensible way to solve - cycles.
class Friend {
Set<Friend> friends();
}
How does one model Me having You as a friend who in turn has me as a Friend back ?
IMMUTABILITY
This class from the outside world should definitely be immutable. The value held internally should be constant for the purposes of equality checks.
[[[ Edit: Added code to demonstrate fully immutable concept ]]]
That's why builders are so nice for immutables - they allow mutability during construction to get everything set before you "freeze" it. In this case, I guess you need a Friend builder that supports creating cycles.
final FriendBuilder john = new FriendBuilder().setName("john");
final FriendBuilder mary = new FriendBuilder().setName("mary");
final FriendBuilder susan = new FriendBuilder().setName("susan");
john
.likes(mary)
.likes(susan);
mary
.likes(susan)
.likes(john);
susan
.likes(john);
// okay lets build the immutable Friends
Map<Friend> friends = FriendsBuilder.createCircleOfFriends(john, mary, susan);
Friend immutableJohn = friends.get("john");
Edit: Added immutable example below to demonstrate approach:
There was some discussion in the comments about whether an immutable version was possible.
Fields are final and immutable. A modifiable set is used in the constructor, but it only the unmodifiable reference is kept after construction.
I have another version that uses Guava ImmutableSet for a truly immutable set rather than JDK's unmodifiable wrapper. It works the same, but uses Guava's nice set builder.
Code:
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
/**
* Note: potentially cycle graph - be careful of deep equals/hashCode/toString/etc.
* Immutable
*/
public class Friend {
public static class Builder {
private final String name;
private final Set<Builder> friends =
new HashSet<Builder>();
Builder(final String name) {
this.name = name;
}
public String getName() {
return name;
}
public Set<Builder> getFriends() {
return friends;
}
void likes(final Builder... newFriends) {
for (final Builder newFriend : newFriends)
friends.add(newFriend);
}
public Map<String, Friend> createCircleOfFriends() {
final IdentityHashMap<Builder, Friend> existing =
new IdentityHashMap<Builder, Friend>();
// Creating one friend creates the graph
new Friend(this, existing);
// after the call existingNodes contains all the nodes in the graph
// Create map of the all nodes
final Map<String, Friend> map =
new HashMap<String, Friend>(existing.size(), 1f);
for (final Friend current : existing.values()) {
map.put(current.getName(), current);
}
return map;
}
}
final String name;
final Set<Friend> friends;
private Friend(
final Builder builder,
final Map<Builder, Friend> existingNodes) {
this.name = builder.getName();
existingNodes.put(builder, this);
final IdentityHashMap<Friend, Friend> friends =
new IdentityHashMap<Friend, Friend>();
for (final Builder current : builder.getFriends()) {
Friend immutableCurrent = existingNodes.get(current);
if (immutableCurrent == null) {
immutableCurrent =
new Friend(current, existingNodes);
}
friends.put(immutableCurrent, immutableCurrent);
}
this.friends = Collections.unmodifiableSet(friends.keySet());
}
public String getName() {
return name;
}
public Set<Friend> getFriends() {
return friends;
}
/** Create string - prints links, but does not traverse them */
#Override
public String toString() {
final StringBuffer sb = new StringBuffer();
sb.append("Friend ").append(System.identityHashCode(this)).append(" {\n");
sb.append(" name = ").append(getName()).append("\n");
sb.append(" links = {").append("\n");
for (final Friend friend : getFriends()) {
sb
.append(" ")
.append(friend.getName())
.append(" (")
.append(System.identityHashCode(friend))
.append(")\n");
}
sb.append(" }\n");
sb.append("}");
return sb.toString();
}
public static void main(final String[] args) {
final Friend.Builder john = new Friend.Builder("john");
final Friend.Builder mary = new Friend.Builder("mary");
final Friend.Builder susan = new Friend.Builder("susan");
john
.likes(mary, susan);
mary
.likes(susan, john);
susan
.likes(john);
// okay lets build the immutable Friends
final Map<String, Friend> friends = john.createCircleOfFriends();
for(final Friend friend : friends.values()) {
System.out.println(friend);
}
final Friend immutableJohn = friends.get("john");
}
}
Output:
Node 11423854 {
value = john
links = {
susan (19537476)
mary (2704014)
}
}
Node 2704014 {
value = mary
links = {
susan (19537476)
john (11423854)
}
}
Node 19537476 {
value = susan
links = {
john (11423854)
}
}
The correct way to model a cycle is with a Graph. And a single source code line comment can be enough to enforce inmutability: "can't touch this".
What kind of inmutable enforcement are you looking for? Do you want a a velociraptor to appear whenever you modify the inmutable Set? The difference between mutable and inmutable is just a convention. However, the bits on the RAM can be easily modified and with the Reflection API you can break any encapsulation and data hiding conventions.
Ignoring the velociraptor for a moment, Java does not support an inmutable type. As a workaround, you need to model a datatype that behaves like one.
And for the inmutable property to make sense you need to make Friend an interface, having one implementing class: InmutableFriend, and the construction of the object should fully happen inside the constructor.
Then, since the graph contains cycles, before creating the final inmutable instances you need to store the graph nodes in some mutable temporary structure. You also need to return an unmodifiableSet on the InmutableFriend.friends() method.
Finally, to clone the graph you need to implement a Deep-copy algorithm like Breadth-first search on the Mutable graph. One question though is what happens when the graph is not fully connected.
interface Friend {
public Set<Friend> friends();
}
class MutableFriend {
private Set<MutableFriend> relations = new HashSet<MutableFriend>();
void connect(MutableFriend otherFiend) {
if (!relations.contains(otherFriend)) {
relations.add(otherFiend);
otherFriend.connect(this);
}
}
Friend freeze() {
Map<MutableFriend, InmutableFriend> table = ...;
/*
* FIXME: Implement a Breadth-first search to clone the graph,
* using this node as the starting point.
*
* TODO: If the graph is not connected this won't work.
*
*/
}
}
class InmutableFriend() implements Friend {
private Set<Friend> connections;
public Set<Friend> friends() {
return connections;
}
public InmutableFriend(Set<Friend> connections) {
// Can't touch this.
this.connections = Collections.unmodifiableSet(connections);
}
}
Immutability doesn't need to be compiler-enforced to be valid architecturaly. You can have a legitimate immutable object that takes post-construction initialization parameters. For instance...
private Object something;
public void init( final Object something )
{
if( this.something != null )
{
throw new IllegalStateException();
}
this.something = something
}
The member field "something" isn't final, but it cannot be set more than once either.
A more complex variant based on discussion in comments...
private boolean initialized;
private Object a;
private Object b;
public void init( final Object a, final Object b )
{
if( this.initialized )
{
throw new IllegalStateException();
}
this.initialized = true;
this.a = a;
this.b = b;
}
public Object getA()
{
assertInitialized();
return this.a;
}
public Object getB()
{
assertInitialized();
return this.b;
}
private void assertInitialized()
{
if( this.initialized )
{
throw new IllegalStateException( "not initialized" );
}
}