Access property after inheriting from an abstract class Java - java

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

Related

How to make a class immutable when its referencing a mutable object?

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.

Java, Making a class Immutable

I came across this exercise online where I have two classes and I'm supposed to make the Tutor class immutable. However, the only thing I can think of is adding final to name field. When it comes to the constructor, I don't think I need to change the initialisation of the name variable as String is immutable. I'm not sure how to approach the collection and how to make this part of the constructor immutable. According to the exercise, I'm not supposed to change the Student class (which I can see is mutable)
public class Student {
private String name;
private 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;
}
}
public final class Tutor {
private String name;
private final Set<Student> tutees;
public Tutor(String name, Student[] students) {
this.name = name;
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;
}
}
The Tutor class presents many aspects promoting its immutability :
the class is final
the Set<Student> is protected against the modifications
no method allowing to change directly the state of the class
However, the defensive copy of the constructor is not complete.
It also has to copy the Students elements of the array passed. Otherwise the client of the constructor may change any instance of them and make so the Tutor instance mutable such as :
Student[] students = ...;
Tutor tutor = new Tutor(name, students);
students[0].setName("new Name!"); // break the immutability of Tutor
You should write something like :
public Tutor(String name, Student[] students){
this.name = name;
tutees = new HashSet<Student>();
for (Student student : students){
Student copy = new Student(student.getName(),
student.getCourse());
tutees.add(copy);
}
}
Additionally note that the Set returned by getTutees() is unmodifiable but elements contained in are as Student is mutable.
So to make Tutor immutable you also have to create a copy of the Student elements as you return getTutees() such as :
public Set<Student> getTutees(){
Set<Student> students = new HashSet<>();
for (Student student : tutees){
Student copy = new Student(student.getName(),
student.getCourse());
students.add(copy);
}
return Collections.unmodifiableSet(students);
}
As you may notice, getting the immutability in these conditions (an instance that we wish immutable but that contains a collection referencing mutable instances) requires to write more code (to read/to maintain/to test) and to perform more processing (so slower to execute).
If Student was an immutable class, the original getTutees() and the original constructor would be enough.
Proper way is to make an object immutable is to:
Declare the object final
Do not provide setter methods
Make all fields private
Make mutable fields final
Use deep copy in the constructor
Clone objects in getter methods, so you don't return actual reference.
Do you really need to return the Set of Students? If you really need that you can hide that by using an interface that provides only getters, something like
interface IStudent {
public String getName();
public String getCourse();
}
class Student : implements IStudent { ...}
and in your Tutor you return Set<IStudent>
To make the Tutor class immutable, you should use the "final" modifier on all the fields inside a Tutor, not on the Tutor's class definition.
Java SE 16
You can use JEP 395: Records feature, introduced as part of Java SE 16, to create an immutable class without requiring much ceremony.
If you have already gone through the above link, you must have figured out that you can do it simply as
record Tutor(String name, Set<Student> tutees) { }
What you get in turn are:
A final class Tutor.
A canonical constructor whose signature is the same as the header, Tutor(String name, Set<Student> tutees).
private final fields, name and tutees and their corresponding public accessor method with the same name and return type.
Automatically created equals, hashCode and toString methods.
Demo:
Student.java
record Student(String name, String course) { }
Tutor.java
import java.util.Set;
record Tutor(String name, Set<Student> tutees) { }
Main.java
import java.util.Set;
class Main {
public static void main(String[] args) {
Set<Student> cscStudents = Set.of(
new Student("Harry", "Java-8"),
new Student("Tina", "Java-9"),
new Student("Andy", "Java-11")
);
Set<Student> scienceStudents = Set.of(
new Student("Tony", "Phy"),
new Student("Kerry", "Chem"),
new Student("John", "Bio")
);
Tutor t1 = new Tutor("Mark", cscStudents);
Tutor t2 = new Tutor("Robin", scienceStudents);
Tutor t3 = new Tutor("Mark", Set.of(
new Student("Andy", "Java-11"),
new Student("Harry", "Java-8"),
new Student("Tina", "Java-9")
)
);
System.out.println(t1);
System.out.println();
System.out.println(t1.tutees());
System.out.println();
System.out.println("Students of " + t1.name() + ":");
t1.tutees()
.stream()
.forEach( t -> System.out.println(t.name()) );
System.out.println();
System.out.println(t1.equals(t2));
System.out.println(t1.equals(t3));
}
}
Output:
Tutor[name=Mark, tutees=[Student[name=Andy, course=Java-11], Student[name=Harry, course=Java-8], Student[name=Tina, course=Java-9]]]
[Student[name=Andy, course=Java-11], Student[name=Harry, course=Java-8], Student[name=Tina, course=Java-9]]
Students of Mark:
Andy
Harry
Tina
false
true

How to create user defined immutable class in Java which is having user defined class object as a class member?

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.

Class Declarations, Constructors and toString methods for all the classes

Question:
Implement a super class Person. Make two classes, Student and Instructor
that inherit from Person. A person has a name and a year of birth. A student
has major and the instructor has a salary. Write the class declarations, the
constructors, and the methods to String for all classes. Write a test program
that tests these classes and methods.
This may not be a complicated one but i'm a beginner in java. Please help me.
I'm getting the following error at the both the constructors 'student()' and 'instructor()'.
"constructor Person in class Person cannot be applied to given types;
required: String,int
found: no arguments
reason: actual and formal argument lists differ in length."
Here's my code:
package One;
class Person{
String name;
int yob;
Person(String s, int d){
name = s;
yob = d;
}
#Override
public String toString(){
return "Name: "+name+"\n Year of Birth: "+yob;
}
}
class Student extends Person{
String major;
Student(String s){
major = s;
}
#Override
public String toString(){
return "The student did his majors in "+major;
}
}
class Instructor extends Person{
int salary;
Instructor(int a){
salary = a;
}
#Override
public String toString(){
return String.format("The salary is ",salary);
}
}
public class Test {
public static void main(String[] args) {
Person p = new Person("Stephen", 1991);
System.out.println(p);
}
}
Thanks in advance.
In your code, the classes Student and Instructor derive from Person. This means that any constructor parameters needed by the base class are also needed by the derived class. When you create a Student, how will the compiler know what to put in the parameters of the Person constructor?
So the code should be like this:
// Student constructor
Student(String majorParam, String nameParam, int yobParam)
{
super(nameParam, yobParam);
this.major = majorParam;
}
...
// Similarly, Instructor constructor
Instructor(int salaryParam, String nameParam, int yobParam)
{
super(nameParam, yobParam);
salary = salaryParam;
}
Notice how we are transferring the constructor parameters required by the base class Person using the super constructor keyword. This always has to be the first line in the derived class constructor.
Then you can construct Student and Instructor as:
Student s = new Student("TheMajor", "TheName", 42);
Instructor i = new Instructor(1000, "TheName", 42);
There is no empty constructor in Person class, so you have to create that and it should work perfectly. Don't forget that super() is called by default unless explicitly called.

java constructor in class cannot be applied to given types

I have 2 subclasses: Staff, Student
they belong to superclass Person.
Here is the code(tasks) which is given by my teacher:
public class Person
{
private String name;
private int yearOfBirth;
/**
* Create a person with given name and age.
*/
Person(String name, int yearOfBirth)
{
this.name = name;
this.yearOfBirth = yearOfBirth;
}
}
class Student extends Person
{
private String SID; // student ID number
/**
* Create a student with no parameters.
*/
Student()
{
//task.
}
}
public class Staff extends Person
{
private String roomNumber;
/**
* Construct a staff member with field values and no pamaeters.
*/
public Staff()
{
//task
}
}
I don't know what can I type in order to create an object without parameter.
It always appears an error like: constructor Person in class Person cannot be applied to given types; required: java.lang.String,int;
I have checked online that there are 2 ways to solve the problem:
add a default value in the superclass: Person()//without parameter.
In the subclass Student:
Student()
{
Person astudent = new Student() //I guess.
}
add a super() in the subclass:
Student()
{
super("xxx")//I guess.
}
I don't know what to do. I an a starter in learning BlueJ.
Hope anyone can help me. Thank you very much.
Since your superclass Person doesn't have a default constructor, in your subclasses (Student and Staff), you must call the superclass constructor as the first statement.
You should define your sub-class constructors like this:
Student() {
super("a_string_value", an_int_value);// You have to pass String and int values to superclass
}
Staff() {
super("a_string_value", an_int_value); // You have to pass String and int values to super class
}
the first thing a constructor will do, is call the constructor (with same arguments) of the super class.
Person does not have a no-argument constructor, so, you must change your code in one of next two ways:
Student(String name, int yearOfBirth)
{
//task.
}
or
Student()
{
super("", 0);
//task.
}
and the same goes for Staff
Add super(NAME_IN_STRING_TYPE,YEAR_OF_BIRTH_IN_INT_TYPE); as a first statement in your subclasse's constructor like
Student constructor
Student()
{
super("name", 1970); // String,int arguments passed
//task.
}
Staff constructor
Staff()
{
super("name", 1970); // String,int arguments passed
//task.
}
This is needed since there is no default no-arg constructor in the base class. You have to explicitly define a no-arg constructor in base class or you need to instruct the compiler to call the custom constructor of the base class.
Note : Compiler will not add default no-arg constructor in a class if it has a user defined constructor. It will add the default no-arg constructor only when there is no constructor defined in the class.
Try this:
Student(String name, int yearOfBirth) {
super(name, yearOfBirth);
// task...
}
Reason: you dont have a default constructor at your superclass. So you have to call super() at the first position in your subclass constructor.
To construct instance of Student you need to do actions neccesary to construct Person first. There is only one way to construct Person - two-arg constructor. That means you have to change Student like:
public Student() {
super("someName", 1950); //first values came to my mind
}
Although you should be aware that Student should behave exactly like Person if treated as Person, i.e. have age and name. So actually I'd recommend to change Student constructor to include name and age there.
If you want to create an object of child class (ie Staff and Student) without passing parameters then you can create an additional constructor without parameters in the parent class (ie Person class) as below.
public class Person
{
private String name;
private int yearOfBirth;
/**
* Create a person with given name and age.
*/
Person(String name, int yearOfBirth)
{
this.name = name;
this.yearOfBirth = yearOfBirth;
}
// additional constructor without parameter
Person(){
// add your code here
}
}
now below code will work without any error.
Staff stf = new Staff();
Student std = new Student();
for constructor no param you should have two constructors like
public class Student {
Student(String name , int dateOfBirth)
{
super(name,dateOfBirth)
}
Student()
{
//task.
}
}
also same for other class
student should not extend person.
bcoz, if we create obj for student, person’s constructor will be called automatically.

Categories