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

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.

Related

How can I get a particular method name as string instead of hardcoding a method name

I have to call a particular method through Java reflection. Instead of passing hardcoded method name, is it possible to pass the method name as a string?
For example
public String getAttribute(Object object1, Object2, String className, String methodName){
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod(methodName);
return ObjectUtils.firstNonNull(null == object1 ? null: method.invoke(object1),null == object2 ? null: method.invoke(object2); }
Let us say I have a class
#Getter
#Setter
Class Student{
String studentName;
String address;
int rollNumber;
}
Lets say, we have caller code
Student student1 = new Student();// Student record from School 1
Student student2 = new Student(); // Student record from School 2
student2.setAddress("ABC");
System.out.println(getAttribute(student1, student2, Student.class.name(), "getAddress"));
Instead of passing hardcoded method name as parameter to getAttribute() method, is there a way that I can use a method name that is not hardcoded?
For example, getAttribute(student, Student.class.name(), Student.class.getStudentName.getName()) so that we can easily make the changes to methods and variable of the student class when required without worrying on hardcoded method name constants.
To find the first non-null result of a given getter of the objects in a collection, you could utilize streams, method references, and optionals, while avoiding reflection entirely.
public static <T, R> Optional<R> findFirstNonNull(Collection<T> objects,
Function<T, R> getter) {
return objects.stream()
.filter(Objects::nonNull)
.map(getter)
.filter(Objects::nonNull)
.findFirst();
}
Example usage: Optional<String> found = findFirstNonNull(fooList, Foo::getName);
public class Foo {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Foo foo1 = null;
Foo foo2 = new Foo();
Foo foo3 = new Foo();
foo3.setName("foo3");
Foo foo4 = new Foo();
foo4.setName("foo4");
List<Foo> fooList = Arrays.asList(foo1, foo2, foo3, foo4);
Optional<String> found = findFirstNonNull(fooList, Foo::getName);
System.out.println(found); // Optional[foo3]
}
}
Note: these are Java 8 features.
you can access annotations at runtime. By marking the method that you want to use with reflection with an annotation it's possible to get all methods and then run the one with an annotation.
Here is a good example of that: java-seek-a-method-with-specific-annotation-and-its-annotation-element
Student student1 = new Student();// Student record from School 1
Student student2 = new Student(); // Student record from School 2
student2.setAddress("ABC");
try {
System.out.println(getAttribute(student1));
} catch (Exception e) {
System.out.println("Some error");
}
public static String getAttribute(Object object) throws Exception{
Method method = getAnnotatedMethod(object.getClass());
return (String) method.invoke(object);
}
public static Method getAnnotatedMethod(final Class<?> type) {
final List<Method> allMethods = new ArrayList<Method>(Arrays.asList(type.getMethods()));
for (final Method method : allMethods) {
if (method.isAnnotationPresent(RunWithReflection.class)) {
return method;
}
}
return null;
}
Then you need the annotation:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class anno {
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface RunWithReflection {
}
}
And then you just annotate the function you want to run with #RunWithReflection and it works.
Another possibility is to create an enum with accessor to each attribute, for example:
public enum StudentAttribute {
NAME,
ADDRESS,
ROLLNUMBER,
;
public Object get(Student s) {
switch(this) {
case NAME: return s.getName();
case ADDRESS: return s.getAddress();
case ROLLNUMBER: return s.getRollNumber();
}
}
}
...
public Object getAttribute(StudentAttribute attr, Student... students) {
if(students==null) return null;
return Arrays.stream(students) //Java 8 Stream
.map(attr::get) //convert into the corresponding attribute value
.filter(o -> o!=null) //we're only interested in non-null values
.findFirst() //specifically the first such non-null value
.orElse(null) //otherwise null
;
}
//access:
Student student1 = new Student();// Student record from School 1
Student student2 = new Student(); // Student record from School 2
student2.setAddress("ABC");
System.out.println(getAttribute(StudentAttribute.ADDRESS, student1, student2));
If passing attribute (method to call) into the main method as a string, you can, for example, pass "ADDRESS" (exact naming as per enum constant) and execute:
public static void main(String[] args) {
Student s1 = ...
Student s2 = ...
...
StudentAttribute attr = StudentAttribute.valueOf(args[0]); //this will be StudentAttribute.ADDRESS
System.out.println(getAttribute(attr, student1, student2));
There aren't any hardcoded Strings here and no reflection, so this will survive any type of refactoring that you can think of.
However, like other solutions this is unnecessarily verbose in that you are more or less duplicating your code for the sake of being able to refactor it later. I'd argue that the completely unnecessary repetition is more of a code smell than having to type "getAddress" more than once and already requires immediate refactoring into something simpler.
Perhaps using Streams, varargs, etc can give you better options for implementing your solution. Time-wise, would you have finished by now if you'd sat down to type everything out, as tedious as that might have been? Food for thought...

Using an object within its own constructor

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);
}
}

Constructor error "call to this must be first statement in constructor"

I am writing some code for a school assignment, and I am not sure why I keep getting the compilation error: "call to this must be first statement in constructor." I'm supposed to create a student with the name Student instead of an inputted name.
public void newStudent( ){
this("Student");
//Error occurs here ^
}
public void newStudent(String student)
{
studentAndStatus[studentCount] [0] = student;
for(int i = 0; i < 3; i++)
{
grades[studentCount] [i] = 0;
}
studentCount++;
}
When you want to create an instance of a class, you should use a constructor. The constructor has the same name as the class and no declared return type, for example
public class Student {
public Student() {
this("Student");
}
public Student(String name) {
//Whatever you want to do...
}
}
In your case the error occurs, because you can only call this in a constructor. Since your class propably is not called newStudent and the method newStudent() has a return type, the method newStudent() is not a constructor and therefore this cannot be called.
I am assuming that newStudent() is a method which is overloaded because constructors can't have a return type.
this(arguments) can be used only for constructor calls, but you are overloading the newStudent() methods, so if you wanted call the newStudent(String student), you can do that as shown below:
public void newStudent( ){
newStudent("Student");//calls the overloaded method
}

Multiple object types in one ArrayList

I have an abstract class called User, a user can be created either as a Student type or as a Teacher type. I have made an ArrayList of users (of students and teachers) and what I am trying to do is call a method example depending on what the current object is an instance of:
for (User user : listOfUsers) {
String name = user.getName();
if (user instanceof Student) {
// call getGrade();
} else { // it is an instance of a Teacher
// call getSubject();
}
}
The problem I'm having is because it is an ArrayList of User objects, it can't get the Student type method, for example, getGrade(). However, because I am able to determine what the current user is an instance of, I'm curious as to whether or not it is still possible to call a specific method depending on what type of user it is.
Is this possible, or do I have to separate the user types into separate lists?
Please reply soon, many thanks.
You'll need to cast them to the class before using the method.
for (User user : listOfUsers) {
String name = user.getName();
if (user instanceof Student) {
Student tmp = (Student)user;
// call getGrade();
tmp.getGrade();
} else { // it is an instance of a Teacher
Teacher tmp = (Teacher)user;
// call getSubject();
tmp.getSubject();
}
}
Check the downcast:
In object-oriented programming, downcasting or type refinement is the
act of casting a reference of a base class to one of its derived
classes.
In many programming languages, it is possible to check through type
introspection to determine whether the type of the referenced object
is indeed the one being cast to or a derived type of it, and thus
issue an error if it is not the case.
In other words, when a variable of the base class (parent class) has a
value of the derived class (child class), downcasting is possible.
Change your code to:
if (user instanceof Student) {
((Student) user).getGrade();
} else { // it is an instance of a Teacher
((Teacher) user).getSubject();
}
Store student and teacher objects in userList and then depending on the instanceOf condition call the respective class method by typeCasting to UserType
Consider below sample code
abstract class User{
public abstract String getName();
}
class Student extends User{
#Override
public String getName() {
// TODO Auto-generated method stub
return "Student";
}
public String getGrade(){
return "First Class";
}
}
class Teacher extends User{
#Override
public String getName() {
// TODO Auto-generated method stub
return "Teacher";
}
public String getSubject(){
return "Java";
}
}
public class Util {
public static void main(String[] args) {
Student s = new Student();
Teacher t = new Teacher();
ArrayList<User> list = new ArrayList<User>();
list.add(s);
list.add(t);
for(User user :list){
if(user instanceof Student){
System.out.println(((Student) user).getGrade());
}
if(user instanceof Teacher){
System.out.println(((Teacher) user).getSubject());
}
}
}
}

Printing an Array that Includes Inheritance members?

I have this issue with my code.
I create a class Employee with two instance variables:
private String name, department;
And afterwards I create another class called Tradesman whichc is an extension of Employee, but with one extra instance variable:
private String trade;
Now what I did is I created a print() method that will: A) in Employee class, print the name and department. B) same method used in Employee, will be created in Tradesman (thus overriding it) that will print extra one element which is the trade. Now the thing is that I create another class called Staff which will contain an array of type Object of some elements, and I have to implement a hire() method, fire() method and a put() method which will print the entire array. Everything is cool and wicked up till the moment of printing. I'm struggling with how to print it, since I have print() method for each class but it must be dependedn on the type... If i cast the method to Employee I will lose the trade element of Tradesman Class, ex:
void put(){
for (Object a:objArray){
((Employee)a).print();
}
}
Which is WRONG. Cause if I hire a Tradesman, this method won't print the Trade. How to deal with this problem? Thanks in advance! Cheers ^^
"Cause if I hire a Tradesman, this method won't print the Trade"
You're wrong, it will. If Employee is actually a Tradesman, calling print() will call Tradesman.print(). If it's overriden, then the overriding method will be called. If all your array objects are Employee, just use an Employee[] instead of Object[] so you don't have to do that useless casting.
"I want to implement an array of both Tradesman and Employee... One array that will contain both, Tradesman and Employee combined."
Employee[] will hold both since a Tradesman is an Employee. Also in Java I suggest you work with a List (List<Employee> in your case), it's more manageable than an array.
Even if you cast a Tradesman to Employee the underlying object will still be of type Tradesman and use the overridden print method.
try this
import java.util.ArrayList;
class Employee {
String name,department;
public Employee(String name, String department) {
this.name = name;
this.department = department;
}
public void print() {
System.out.println("Name: " + this.name);
System.out.println("Department: " + this.department);
}
}
class Tradesman extends Employee {
String trade;
public Tradesman(String name, String department, String trade) {
super(name,department);
this.trade = trade;
}
public void print() {
super.print();
System.out.println("Trade: " + this.trade);
}
}
class Staff {
ArrayList<Employee> empArray = new ArrayList<Employee>();
public void put() {
for(Employee emp : empArray) {
emp.print();
}
}
public static void main(String[] arg) {
Staff s = new Staff();
s.empArray.add(new Employee("John","Sales"));
s.empArray.add(new Tradesman("Jacob","Sales","Computers"));
s.put();
}
}

Categories