Using an object within its own constructor - java

Is it possible (or sensible) to use an object in it's own constructor?(Sorry for the poorly formulated noob question)
Say I have a class "Students" which contains an arrayList of subclass Student and a method for adding new students to the array.
Can I in my Student constructor use the addStudent method to add the new instance to the array on creation?... like so:
//Students
class Students{
private static ArrayList<Student> STUDENTS = new ArrayList<>();
public static void addStudents(Student student){
STUDENTS.add(student);
}
}
//Student
class Student /*extends Students <- old misstake left for reference*/{
private String name = "";
private int birthYear = 0;
Student(String _name, int _birthYear){
this.name = _name;
this.birthYear = _birthYear;
//insert wild guess
Students.addStudents(this(name,birthYear));
}
}
Or will this simply loop and create a lot of objects until everything crashes?

You can; you just shouldn't.
One reason is that you might not always want to add all Student instances to the same shared list. For example, if you're creating Student instances in a unit test, and adding them into the Students list in the constructor, you then have to worry about clearing the list after the test, to avoid accidentally sharing state between tests.
Another reason is that adding the instance in the constructor is called unsafe publication. You are giving out a reference to an instance which has not been fully initialized. This can lead to some very tricky bugs, especially related to concurrency.
You should always wait until an instance has been fully initialized (i.e. one new Whatever has returned) before you do anything with it.
You would be better decoupling creating the Student from adding to the Students list. Use a factory method to create students:
class Students{
private static ArrayList<Student> STUDENTS = new ArrayList<>();
public static void addStudents(Student student){
STUDENTS.add(student);
}
// Factory method.
public static Student createAndAddStudent(String name, int birthYear) {
Student student = new Student(name, birthYear);
addStudents(student);
return student;
}
}
In terms of your current code, you don't need extends Students. Logically, Student isn't a Students, any more than a Car wouldn't be a Cars (say it out loud; it just doesn't make sense).
All you need to do is to invoke the static method:
class Student {
Student() {
// ...
Students.addStudents(this);
// ...
}
}

You are using the keyword this incorrectly. You don't send any values along with this. this is a reference to the current object.
The benefit of using this way is that you'll never had to individually add all of the students to a list. Along with this it will save you the hassle of accidentally forgetting to add one to your array.
class Student extends Students{
private String name = "";
private int birthYear = 0;
Student(String _name, int _birthYear){
this.name = _name;
this.birthYear = _birthYear;
addStudents(this);
}
public static void main(String[] args){
Student s = new Student("Ryan", 1999);
}
}

Related

Java OOP; creating several objects

I am trying to store students' data in main class, in the student class I did the following:
public class Student {
public static String name = "UNKNOWN";
Student(){
}
Student(String name) {
this.name = name;}
While in main I did the following:
public class Main {
public static void main (String[] args) {
//s1 is short for student1
Student s1 = new Student ("Chris");
Student s2 = new Student ("Layla");
Student s3 = new Student ("Mark");
This issue is, whenever I print a sx.name I'd always print the last one. So for the following code:
System.out.println(s1.name);
I'd get Mark, while it should be Chris.
Yes, Because static fields are shared by all the objects.
Please change your code as follows:
public class Student {
public String name;
Student(){
}
Student(String name) {
this.name = name;
}
public static void main (String[] args) {
//s1 is short for student1
Student s1 = new Student ("Chris");
Student s2 = new Student ("Layla");
Student s3 = new Student ("Mark");
}
When to use static in java?
Sometimes, you want to have variables that are common to all objects.
This is accomplished with the static modifier. Fields that have the
static modifier in their declaration are called static fields or class
variables. They are associated with the class, rather than with any
object. Every instance of the class shares a class variable, which is
in one fixed location in memory. Any object can change the value of a
class variable, but class variables can also be manipulated without
creating an instance of the class.
The static variable name is accessed by instances, which is not recommended in Java. You can make it non-static. The IDE, such as IDEA, will give you some hint/warning about this issue, like:
So with the help of IDEs, you can easily find some potential issues.

Creating subclass object in an array which type of superclass in java

I'm trying to cover OOP stuffs but I stuck here. I get error in Company class, employees[0].setBonus(50) part. Isn't there any other way to solve it except defining Bonus methods in Employee class? Or is there anyway to keep all objects in one array? I defined bonus methods but there was another thing, what I have to return in Empoyee class' getBonus method?
public class Company
{
private static Employee[] employees;
public Company()
{
employees= new Employee[]{new Manager("Sapo",10000),new Employee("James",5000),new Employee("Jessie",5001)};
}
public static void main(String[] args)
{
Company company= new Company();
employees[0].setBonus(50);
System.out.println(employees[0].getBonus());
}
}
public class Employee extends Person
{
int salary;
public Employee(String name,int salary) {
super(name);
setSalary(salary);
// TODO Auto-generated constructor stub
}
public void setSalary(int salary)
{
this.salary= salary;
}
public int getSalary()
{
return salary;
}
}
public class Manager extends Employee
{
private int bonus;
public Manager(String name, int salary) {
super(name, maas);
}
public void setBonus(int bns)
{
bonus=bns;
}
public int getBonus()
{
return bonus;
}
public int getSalary()
{
return salary+bonus;
}
}
I'm confused.
If you really want do it this way, you can cast employee[0] as manager, but You must know is not nice solution. E.g:
Company company= new Company();
Manager manager = Manager.class.cast(employees[0]);
manager.setBonus(50);
System.out.println(manager.getBonus());
Or is there anyway to keep all objects in one array?
You could but it would force you to write :
if ( employees[0] instanceof Manager){
((Manager) employees[0]).setBonus(50);
}
which is not a good practice.
Functionally, if a bonus is a property which owns only the Manager instances, an Employee instance should not try to set or get it.
When you do :
employees[0].setBonus(50);
the compiler doesn't know the concrete instance. It sees only Employee.
In this very simple code, we see straight that the first employee is a manager but in a real application the array may be modified multiple times. Trying to remember at which indexes are the managers is error prone. If you need to call a manager specific method on a or several managers you should be sure about knowing which variables are managers. So declaring them as manager seems the more natural way.
To solve your problem, two arrays seems more interesting : one for employees and another for managers :
private static Employee[] employees;
private static Manager[] managers;
Now you can do :
public Company()
{
employees= new Employee[]{new Employee("James",5000),new Employee("Jessie",5001)};
managers= new Employee[]{new Manager("Sapo",10000)};
}
public static void main(String[] args){
Company company= new Company();
managers[0].setBonus(50);
System.out.println(managers[0].getBonus());
}
Your abstraction is wrong. You see, the compiler only has that information that is available at compile time.
You have an array of Employee objects there. It doesn't matter that your code at run time will put a Manager object into that array! The compiler doesn't know that. He only knows that there is an Employee.
And the Employee class has no method setBonus(). Thus you can't call that method!
One possible solution would be for example to make the bonus a parameter to the constructor of Manager; the whole think could look like this:
public class Manager extends Employee {
private final int bonus;
public Manager(String name, int salary, int bonus) {
super(name, salary);
this.bonus = bonus;
}
#Override
int getSalary() {
return super.getSalary() + bonus;
}
Notes:
You should avoid to use fields of your super class; those are private implementation details of that class. Child classes should not care about them. Instead, you could call the method from the super class.
On the other hand, you should strive for making your fields final. That makes a lot of things much easier.
When overriding methods ... use the #Override annotation!
Finally: a constructor is a bad place to create your "test data". In other words: your main method is to place to create that array of employees; and then you just pass that into your Company constructor. You want to clearly separate your "real business logic" from that stuff that mainly exists to test that "business logic".

ArrayList of class as parameter

StackPeople, I have a question. What statement could help me implement the right class before inserting it to the ArrayList. I have declared Nurse and Pilot which are Employees objects.
I want each implementation of my class ArrEmp to store different Employees objects
example: arrEmpNurses, arrEmpPilots,... after my class gets an example in the constructor
What statement helps?? Or should I re think the problem.
Thanks for your help.
THE PROBLEM IS TO FILL THE ARRAY WITH THE RIGHT CLASS (IT WILL READ FROM PLAIN TEXT AND IT NEWS TO BE NOTIFIED WhAT CLASS TO IMPLEMENT TO ADD IT)
"This code compiles, just copy paste."
import java.util.*;
public class ArrEmp {
String[][] data={ {"E1"}, {"Maria"}, {"E2"}, {"John"} }; //Data
Employee x;
static Nurse nancy= new Nurse("01","Nancy");//this are just examples
static Pilot peter= new Pilot("02","Peter");//so the arrayEmp knows what type of employee create
ArrayList arr;
public ArrEmp(Employee x){
this.x=x;
arr= new ArrayList();
fillList();//with data array
}
public void fillList(){// I would like to fill the List with Nurses. How could i do it?
//for( String[] param: data )
//arr.add( ) //insert helpfull statement here
//the goal is to have an array of Pilot and another of Nurses
}
public static void main(String[] args) {
ArrEmp arr1= new ArrEmp( nancy );
ArrEmp arr2= new ArrEmp( peter );
}
public static class Employee {
String cod;
public Employee(String cod){
this.cod=cod;
}
}
public static class Nurse extends Employee{
String name;
public Nurse(String ... para){
super(para[0]);
this.name=para[1];
}
}
public static class Pilot extends Employee{
String name;
public Pilot(String ... para){
super(para[0]);
this.name=para[1];
}
}
}
I asked the question this way because data is actually read from Disk and ArrEmp has no idea what Employee he is reading. i need to provide an example so it builds the right employee and then insert it into the array. so new ArrEmp( nancy ) reads the file and builds Nurses and store them but new ArrEmp( nancy ) reads a file and loads Pilots on it.
EDIT SOLUTION: ESCENTIALLY I WILL CREATE A GENERIC ARRAYLIST EXTENDS EMPLOYEE, and extending classes for each Emlployee object...
Why not use generics? See: Java generics - ArrayList initialization
Essentially use
ArrayList<Nurse>
Instead of ArrayEmp(Nancy) to say it will only contain Nurses, then the language will take care of enforcing it.
public static class Employee {
String name;
int ID = 0;
public Employee(String name){
this.name = name;
}
}
Just use ID's to denote the differentiation between all of them? You can create an ENUM and fill in legible names for differentiating between different objects. It's faster then string comparing and using instanceOf.
public static class Pilot extends Employee{
int ID = 1;
public Pilot(String name){
this.name = name;
}
}
EDIT:
public ArrEmp(Employee x){
if (x.ID == 1) // add to the list you want
else if (x.ID == 2) // add to list you want
....
}

Cannot make a static reference to the non-static method

I have searched for this problem and found many answers regarding it but however i did not understand them , i would a clarification regarding my own code so hopefully it will makes sense
i am trying to call the PrintList method in the main method
but i get this error
Cannot make a static reference to the non-static method PrintList() from the type Stack
if i change the modifier of PrintList to static , it ruins the whole code.
can anyone help me fix this issue please?
Thanks
public class Stack<Item> {
public int N; // size of the stack
public Node<Item> first; // top of stack
public Node<Item> last; // top of stack
// helper linked list class
private static class Node<Item> {
private Item item;
private Node<Item> next;
}
public Stack() {
first = null;
last = null;
N = 0;
}
public void PrintList() {
Node<Item> current;
current = first;
while (current.next != null) {
System.out.println(current.item);
current = current.next;
}
}
public static void main(String[] args) {
// Declare the stack
Stack<String> s = new Stack<String>();
s.push("Bob");
s.push("Mary");
s.push("David");
s.InsertBegin("George");
System.out.println("First item: " + s.peek());
Object current;
PrintList(); // what is wrong here?
}
}
The problem is that you are not specifying the instance that PrintList is to be called on. To fix that, change this:
PrintList(); // what is wrong here?
to this:
s.PrintList();
What you really need is to understand exactly what static and non-static actually mean.
First, some background. Apologies if some of this is already familiar to you. Java is an object oriented language, you create a class to act as a template for a specific type of object, defining what attributes (variables) that it has, and how it behaves (methods). These attributes and behaviours belong to objects of that class:
public class Person {
private String forename;
private String surname;
public Person(String forename, String surname) {
this.forename = forename;
this.surname = surname;
}
public String getFullName() {
return forename + " " + surname;
}
public static void main(String[] args) {
Person john = new Person("John", "Doe");
}
}
The above code defines a template for creating objects of the type Person, each having a forename and a surname, both of the type String. It also defines a behaviour that allows you to get a Person's full name using the getFullName() method.
Both forename and surname, as well as getFullName() are examples of non-static fields/methods. That is, they belong to a specific Person object. Importantly: none of these can exist without a Person object being created first. In this case we have a Person object called john which has a forename of "John" and a surname of "Doe". If we were to call john's getFullName() method:
john.getFullName();
Then we'd get "John Doe" back.
The opposite of this is static. Static things do not belong to an object, instead, they belong to a class.
public class Person {
private String forename;
private String surname;
private static String species = "Homo sapiens";
public Person(String forename, String surname) {
this.forename = forename;
this.surname = surname;
}
public String getFullName() {
return forename + " " + surname;
}
public static void main(String[] args) {
Person john = new Person("John", "Doe");
}
}
Here the String species doesn't belong to john, it belongs to Person. Static methods and variables don't need an object in order to exist, they always* exist. You access it by using the class itself as a reference, like this:
Person.species;
In your example, you have defined a method PrintList() as a behaviour of objects of the Stack<Item> class. The problem is that you're inside the main method, which is static. This means that you aren't in the 'context' of an object (because main belongs to Stack<Item>, not objects of the type Stack<Item>) when you're trying to call the PrintList() method. When you're inside a static method, in order to call a non-static method or access a non-static attribute, you must do so using a reference to an object of the class that owns it. In your case, you already have this reference in the form of s, so you can call your PrintList() method like so:
s.PrintList();
NB: Conventionally in Java we use camelCase for method names, so it really should be printList().
When I first started to learn Java, I found the concept of static very difficult to wrap my head around - because I hadn't learned to think in an object-oriented way yet. When the penny drops, you'll wonder why you ever struggled with it. Hopefully this will help you get closer towards that penny-drop moment!
*As long as the class is loaded and it's not a compile-time constant (but you don't need to worry about those yet).
You can call static methods like this:
ClassName.methodToCall();
You can call non-static methods like this:
ClassName classInstance = new ClassName();
classInstance.methodToCall();
Since your method PrintList() is non-static as it should be in this case, you should call it on an instance.

Why method uses class name as modifier(?) and argument?

Below are the 4 methods that I got confused, that 4 methods are in the Teacher class. I have a Student class and Teacher class. and in the Teacher class, declared is the ArrayList<Student> students as instance variable.
How to explain the Student that I saw in the methods given below and it is also used as a parameter. I am very confused with the Student searchStudent (in the method) and the Student student (inside the argument). Is that for ArrayList only? How to understand that concept where one class will search another class using the class name?
public Student searchStudent(Student student)
{
//confuses me
Student found = null;
if (this.students.contains(student))
{
int index = this.students.indexOf(student);
if (index != -1)
{
found = this.students.get(index);
}
}
return found;
}
public Student searchStudent(int id)
{
//confuses me
Student beingSearched = new Student();
beingSearched.setStudentId(id);
return this.searchStudent(beingSearched);
}
public boolean addStudent(Student student)
{
//confuses me
boolean added = false;
if (this.searchStudent(student) == null)
{
this.students.add(student);
added = true;
}
return added;
}
public boolean addStudent(int id, String name, double grade)
{
//this is fine as i know boolen and int, String and double//
Student student = new Student(id, name, grade);
return this.addStudent(student);
}
I advice you to go through this link about Defining Methods.
public Student searchStudent(Student student)
It's a public method that returns an object of type Student, it accepts an object of type Student as well.
It needs to accept the student parameter because it search for it. You'll use this method when you want to search whether some student exists in your records (in the student ArrayList).
public Student searchStudent(int id)
The same, but the parameter it accepts is an int. Here you'll search for the student not by the object itself, but by the id of the student.
public boolean addStudent(Student student)
It's a method that adds a student (which is of type Student) to students ArrayList.
Tip: Run your code in Debug mode and follow each method you don't understand, you'll be amazed of how much this will help you to better understand the flow of the program.

Categories