Let's say I have a class in java called Employee that looks something like this
public class Employee {
private String empName;
private int empId;
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
}
Now I want to use this object in an immutable class let's say a company in the following format. and the condition is that I cannot modify
public final class Company {
final String companyName;
final Employee employee;
public Company(String companyName, Employee employee) {
this.companyName = companyName;
this.employee = employee;
}
public String getCompanyName() {
return companyName;
}
public Employee getEmployee() {
return employee;
}
}
So my question is, is this a valid way to make Company class immutable when I am referencing an inside object that can be modified?
As referenced in this article https://www.journaldev.com/129/how-to-create-immutable-class-in-java do a deep cloning of Employee object in your constructor of final class. This way you will won’t use the object reference.
2 things that came to my mind:
Add a ReadOnlyEmployee Interface for your Employee which only exposes the getters. Then you would have to change the return type of getEmployee() to ReadOnlyEmployee. The advantage of this solution is that it's clear and explicit for the user. The problem is that the getter returns another type than the constructor accepts which may be confusing.
Add a proxy class that extends the Employee class that throws an IllegalAccessException or similar on setter calls. The advantage is that you do not have to introduce new Interfaces or change the methods of Company. The disadvantage is the possible runtime Exceptions.
Technically, no. Adding final makes the reference immutable: you cannot assign a different Employee object. this.employee = ... is impossible.
However, finality isn't contagious the way constness is in C++. It's still possible to call getEmployee().setEmpName(...) or getEmployee().setEmpId(...) and modify the employee object. You can't replace it with a new one but you can modify the object that's there.
If you want to make Company completely immutable then you need to make defensive copies of the Employee object in two places. One, you need to copy the object passed in the constructor. Two, you need to return a copy from getEmployee() to prevent the internal object from being exposed.
public final class Company {
final String companyName;
final Employee employee;
public Company(String companyName, Employee employee) {
this.companyName = companyName;
this.employee = new Employee(employee); // 1
}
public String getCompanyName() {
return companyName;
}
public Employee getEmployee() {
return new Employee(employee); // 2
}
}
The problem is that you release a reference to an employee instance, thus the caller may modify the object.
You return a link to a copy of the employee and stop worrying about what will happen next. You protected the underlying instance. The caller can do whatever they want with a copy, while your field remains consistent and effectively unchanged (in fact, it's changeable, of course).
public class Employee {
public Employee(Employee o) {
// copy evething you need from o
}
}
public final class Company {
public Employee getEmployee() {
return new Employee(employee);
}
}
Problems here? The caller is altering the employee's data and can't figure out why nothing has been changed within the company.
You return a reference to a Company's inner subclass of Employee. In this class, you override setters and other methods that change the state. The caller, for instance, might be getting an UnsupportedOperationException when they call such modifying methods on a retrieved Employee.
public final class Company {
private final CompanyEmployee companyEmployee;
public Company(String companyName, Employee employee) {
this.companyName = companyName;
companyEmployee = new CompanyEmploye(employee);
}
private static class CompanyEmployee extends Employee {
public Employee(Employee o) {
super(o);
}
public void setEmpName(String empName) {
throw new UnsupportedOperationException();
}
public void setEmpId(int empId) {
throw new UnsupportedOperationException();
}
}
public Employee getEmployee() {
return companyEmployee;
}
}
Problems here? Inheritance is used to control access.
Otherwise, an immutable class that is made of mutable components isn't that immutable.
Is this a valid way to make Company class immutable when I am referencing an inside object that can be modified?
No. From my understanding, any component obtained from an instance of an immutable class shouldn't be alterable. No matter at what level a request to change may occur.
Related
I cannot access a field of a class that is a concrete type inheriting from an abstract class.
In Java I create a class of External student that extends Student
*/
public class ExternalStudent extends Student {
String currentSchool;
public ExternalStudent(String name, Integer age, String studentIdentifier, String currentSchool) {
super(name, age, studentIdentifier);
this.currentSchool = currentSchool;
}
}
where student is
public abstract class Student {
//Attributes
String studentIdentifier;
Integer age;
String name;
//Associations
List<Subject> subject = new ArrayList<Subject>();
PersonalDetails personaldetails;
//Methods
public void setSubject () {
this.subject.add(new Subject("Name"));
}
//Constructors
public Student(String name, Integer age, String studentIdentifier){
this.age = age;
this.name = name;
this.studentIdentifier = studentIdentifier;
}
}
and external student is set up by my class Application
public class ApplicationC {
//Attributes
private String personalStatement;
private String applicationForm;
//Associations
Registration registration;
Student student;
ApplicationTest applicationtest;
//Methods
public void setApplicationResult(String result){
this.applicationtest = new ApplicationTest(result);
}
//Constructor
public ApplicationC (String personalStatement, String name){
this.registration = new Registration();
this.student = new ExternalStudent("Tom",16,"78954","DHS");
}
}
I've set up a simple test class
public void testPostCondition() throws ParseException{
ApplicationC instance = new ApplicationC("test statement","test name");
instance.setApplicationResult("pass");
assertEquals("pass",instance.applicationtest.result);
instance.student.age = 16;
instance.student.studentIdentifier = "78954";
instance.student.name = "Tom";
instance.student.currentSchool = "test"; //Error as field does not exist
}
But I cannot access the current school of the student instance (who must be an externalStudent). How can I access this field in order to test my code?
In ApplicationC, the student field is declared with the Student class :
Student student;
Methods available on an objects relies on the declared type, not the object really instantiated.
And currentSchool is only declared in the subclass ExternalStudent.
So, you cannot access it in this way.
A workaround is downcasting Student to ExternalStudent :
((ExternalStudent)instance.student).studentIdentifier = "78954";
And generally, it is better to check the type of the instance before doing it :
if (instance.student instanceof ExternalStudent){
((ExternalStudent)instance.student).studentIdentifier = "78954";
}
As a general advice, in Java, you should favor the private modifier for fields and if you need to manipulate the base class and access to some fields specific to the subclass, you could define a method in the base class that returns null or Optional and override it in the subclass with the return of the field.
It avoids cast that may be error prone and that often are symptoms of a conception problem.
Your instance is an AplicationC,
So, "instance.student" is a "Student".
"Student" does not have the "currentSchool" property.
to get to it
* add "currentSchool" property to "Student"
or
* cast your "instance.student" to "ExternalStudent"
note: you will need to handle all the exceptions and over-head of casting etc'
Hope this helps
I want to make my immutable class EmployeeDetails which has Employee object in it. I have followed conditions to make class immutable:
1. class is final
2. class members are final
3. no setters
If EmployeeDetails is immutable, I should not be able to change content in it. Still, I can change employee name or id.
What I am missing here?
public class TestImmutable{
public static void main(String args[]){
EmployeeDetails empd1 = new EmployeeDetails("ABC", new Employee(1, "n1"));
System.out.println("Id : " + empd1.getEmployee().getId());
System.out.println("Name : " + empd1.getEmployee().getName());
System.out.println("Empr : " + empd1.getEmployer());
empd1.getEmployee().setId(2);
empd1.getEmployee().setName("n2");
System.out.println("\nId : " + empd1.getEmployee().getId());
System.out.println("Name : " + empd1.getEmployee().getName());
System.out.println("Empr : " + empd1.getEmployer());
}
}
final class EmployeeDetails{
private final String employer;
private final Employee emp1;
public EmployeeDetails(String employer, Employee emp1){
this.employer = employer;
this.emp1 = emp1;
}
public String getEmployer(){
return this.employer;
}
public Employee getEmployee(){
return this.emp1;
}
}
class Employee{
public int id;
public String name;
public Employee(int id, String name){
this.id = id;
this.name = name;
}
public int getId(){
return this.id;
}
public String getName(){
return this.name;
}
public void setId(int id){
this.id = id;
}
public void setName(String name){
this.name = name;
}
}
Get rid of getEmployee(). You shouldn't be able to reach Employee emp1 outside EmployeeDetails. If you need access to the fields in Employee emp1, then provide public methods that return them.
For example:
final class EmployeeDetails{
private final String employer;
private final Employee emp1;
public EmployeeDetails(String employer, Employee emp1){
this.employer = employer;
this.emp1 = emp1;
}
public String getEmployer(){
return this.employer;
}
public String getEmployeeName() {
return this.emp1.getName();
}
...
}
I have followed conditions to make class immutable: 1. class is final
2. class members are final 3. no setters
The conditions you have laid out are necessary but not sufficient to make a class immutable. Confused?
Immutability is about preserving the state of class instances for all time. Once an instance of a class is created, then all the attributes that comprise the state of that instance must remain forever unchanged.
What happens if 1 through 3 above are satisfied, but one of your instance fields is a mutable class? In this case, returning the reference to that instance field to a client makes it possible for the client to mutate the state of your supposedly immutable class.
One solution is to perform defensive copying upon all instance fields of the immutable class that are, themselves, mutable. Instead of...
public Employee getEmployee(){
return this.emp1;
}
change this code so that a new copy of the Employee object is returned to the client. This assures that clients can not get a reference to the internal state of the instances of your immutable class:
public Employee getEmployee(){
return this.emp1.clone(); // this solution assumes that Employee
// is safely cloneable, which requires some
// care on your part. An alternative is
// to define a copy constructor in the
// Employee class and: return new Employee(emp1);
}
Defensive copying is necessary for all mutable components of an immutable class, and this rule must be applied during both construction and field access. Otherwise, you make it possible for client code to retain a reference to the mutable internal state of your class.
Making a variable final means that you cannot assign it again to some other object. You can still modify the state of the object whose reference it holds.
In this case :
final class EmployeeDetails{
private final String employer;
**private final Employee emp1;**
}
You cannot assign emp1 to a new object but you can still change the state of the employee object as it is not immutable. You can make Employee objects immutable by removing all the setters.
The EmployeeDetails class is not immutable. You have followed the usual rules for immutability except one. In Joshua Bloch's Effective Java this rule is stated as follows:
Ensure exclusive access to any mutable componenent.
In your case, since the class Employee is mutable, you need to copy Employee instances in the getter and the constructor.
public Employee getEmployee() {
return new Employee(emp1.getId(), empl.getName()); // Copy
}
public EmployeeDetails(String employer, Employee emp1){
this.employer = employer;
this.emp1 = new Employee(emp1.getId(), empl.getName()); // Copy
}
Since this version uses copies, it is not possible to modify the internals of the EmployeeDetails class.
This solution is very common. For example, the String class is immutable. Both the constructor String(char[] value) and the method char[] toCharArray() make copies of the array. This is necessary because arrays are mutable.
Another solution, which may be more appropriate for your situation, is to make Employee immutable as well.
You could even get rid of the Employee class completely and just use two fields in the EmployeeDetails class.
So I'm kinda curious how can I store for example a list of items in my class with prices, code, quantity and so on.
Is there an easy way to have it all related and not creating a function for each variable?
You can define a List object in your class.
For example a person that has credit cards:
public Class Person {
private String name;
private List<CreditCard> creditCards;
// toString, equals, constructor, etc.
public void setCreditCards(List<CreditCard> creditCards) {
this.creditCards = creditCards;
}
public List<Creditcard> getCreditCards() {
return this.creditCards;
}
// more getters and setters
And the Credit Card:
public Class CreditCard {
private String number;
private Date expiryDate;
// getters and setters
}
And then you can make calls like this:
Person person = new Person();
List<CreditCard> cards = person.getCreditCards();
for(CreditCard card: cards) {
String number = card.getNumber();
}
A Java class can be thought of as a collection of fields (variables), and methods (functions).
The way you make a class hold more than one piece of data is to add more fields, and more methods to access those fields.
For example:
public class Cat {
int id;
String name;
double price;
public Integer getId() {
return this.id;
}
//........etc
}
You can then store that in your list and access different bits of data.
I am new to Java and working in Employee management system . I have created few classes including Employee(name, dob etc), Department (dept name, description etc). I have a requirement that Department must have 2 empoyee and less than 10.
Can anyone tell me how to make that association?
Department class:
public class Department {
private String departmentName;
private String locationofDep;
Employee emp = new Employee()
Getter.. setter
}
public class Employee {
private String empName;
private String dob;
Getter.. setter
}
According to the question you should have a collection of Employee object. So you have to create Employee Collection such List, Set and etc. However you cannot limit the capacity a Collection such between two values. Programmatically only thing you can do is throw an Exception when calling the getter method of the Collection. You can put two default value to the Collection. I don't believe that would work for you. Do following modifications in your code.
public class Department {
private String departmentName;
private String locationofDep;
//collection of empleyees
Set<Employee> employees = new HashSet<>();
//use this method to add employees
public void addEmployee(Employee employee) {
this.employees.add(employee);;
}
public Set<Employee> getEmployees() throws Exception {
if (this.employees.size() < 2 || this.employees.size() > 10) {
throw new Exception("Employees out of capacity");
}
return this.employees;
}
//other getters and setters should be here
}
public class Employee {
private String empName;
private String dob;
//put getters and setters
}
So you can manage the employee length problem when you getting employee information using try-catch blocks
I have two classes, Person and Employee. Employee extends Person.
I have a method that reads a Person from storage, and am writing the method that reads an Employee.
I'd like to reuse the method I have for Person to read the properties that are the same for Employee, without copy-pasting the code, but can't seem to find a way to do it.
public Person getPersonFromStorage() {
Person person = new Person();
// ... logic
return person;
}
public Employee getEmployeeFromStorage() {
Employee employee = new Employee();
// ... logic for employee-specific fields
// I want to read the fields inherited from Person here
return employee;
}
I cannot cast the retrieved Person from getPersonFromStorage, because it is not an Employee. It could be, because it's not another subtype either, but it's not.
I could do the following:
public Person getPersonFromStorage(Person person) {
if(person==null) { person = new Person(); }
// ... logic
return person;
}
public Employee getEmployeeFromStorage() {
Employee employee = (Employee) getPersonFromStorage(new Employee());
// ... logic for employee-specific fields
return employee;
}
But I'd like to avoid this complexity if I can. I have the feeling I'm overlooking something elementary. Is there a better way to solve this?
Just offering a different architecture that I usually use in this instance. If you're talking about "from storage", to me that means some sort of persistent structure. Text File, database, etc. For the following example, let's assume you have your values in a text file.
Assume a file employees.txt, which contains one employee:
// Dave's person details.
Dave
Manchester
32
// Dave's employee details
Assassin
Mostly North Korea.
Then you've got a class Person, that looks a little like this:
public class Person
{
private String name;
private String location;
private int age;
public Person(String name, String location, int age)
{
// blah blah blah.
}
}
And a class Employee that looks like this:
public class Employee extends Person
{
private String jobTitle;
private String area;
public Employee() {
// etc.
}
}
In your Person class, you can create a constructor designed to read the parameters for a Person. Something like:
public Person(Scanner file)
{
this.name = file.nextLine();
this.location = file.nextLine();
this.age = file.nextInt();
file.nextLine(); // Make sure you're pointing at the new line!
}
And in your Employee class, you can create a constructor designed to read the parameters for an employee, while calling it's superclass to deal with the other values.
public Employee(Scanner file)
{
super(file);
this.jobTitle = scanner.nextLine();
this.area = scanner.nextLine();
}
Then all you have to do to call this is something like:
Scanner s = new Scanner("employees.txt");
Person p = new Employee(s);
or to make it more compact:
Person p = new Employee(new Scanner("employees.txt"));
This will go and parse the file, and return an object, while wrapping up all the logic for actually reading the file inside the classes that need the data.
Not a text file?
Well, it's not really vital. The important thing is just passing an object up the call chain, and those methods are doing what that particular class needs to do, then passing on the object. If it's a database row, it's exactly the same principle.
Your second code example is the way to go, except you don't even need the null check line. Just pass in a non-null Person that you've instantiated elsewhere.
For even better abstraction, see if you can make Person into an abstract class.
More elegant way is to overload Employee constructor to be able to create Employee instance from parent Person instance.
public Employee getEmployeeFromStorage() {
Employee employee = new Employee(getPersonFromStorage());
// ... logic for employee-specific fields
return employee;
}
You could use a protected factory method.
protected Person createNewInstance() { return new Person(); }
and use this in your getPersonFromStorage() method. The subclass would then override this method, thus changing the return type into an Employee, which you can then use like in your second example.
public class Person {
public Person getPersonFromStorage() {
Person person = createNewInstance();
// ... logic
return person;
}
protected Person createNewInstance() {
return new Person();
}
}
public class Employee extends Person {
public Employee getEmployeeFromStorage() {
Employee employee = (Employee) getPersonFromStorage();
// ... logic for employee-specific fields
return employee;
}
protected Person createNewInstance() {
return new Employee();
}
}
Alternatively you could also create an Employee constructor based on a Person
public class Employee extends Person {
public Employee(Person person) {
super();
// copy all fields from person
}
public static Employee getEmployeeFromStorage() {
Employee employee = new Employee(getPersonFromStorage());
// ... logic for employee-specific fields
return employee;
}
}
I also added static to the methods, assuming you're intending to create new instances with no direct relationship to existing objects. This would not work with the first variant.