Avoiding recursion in toString method - java

this is my huge mindblowing problem with a Java exercise. So, we've got this:
public class CyclicEmployee {
private int age;
private String name;
private CyclicEmployee boss;
private List<CyclicEmployee> subordinate
}
and our goal is to overwrite toString method by cutting fields which may lead to recursive infinity. Finally it's supposed to look like a printed object with a name, age, boss and subordinate.
Employee[age=30,name='Mike',boss=Employee[age=45,name='Ann'], subordinate=[Employee[age=25,name='Jimmy']]]
Well, I tried and found that i have no clue how to deal with toString overriding:
import java.util.ArrayList;
import java.util.List;
public class CyclicEmployee {
private int age;
private String name;
private CyclicEmployee boss;
private List<CyclicEmployee> subordinate ;
public CyclicEmployee(int age, String name) {
this.age=age;
this.name=name;
}
public static void main(String[] args) {
CyclicEmployee Mike = new CyclicEmployee(33,"Mike");
Mike.boss = new CyclicEmployee(44,"Ann");
Mike.subordinate = new ArrayList<CyclicEmployee>();
Mike.subordinate.add(new CyclicEmployee(24,"Jim"));
System.out.println(Mike.toString());
}
#Override
public String toString() {
return "CyclicEmployee{" +
"age=" + age +
", name='" + name + '\'' +
", boss=" + boss +
", subordinate=" + subordinate +
'}';
}
}CyclicEmployee{age=33, name='Mike', boss=CyclicEmployee{age=44, name='Ann', boss=null, subordinate=null}, subordinate=[CyclicEmployee{age=24, name='Jim', boss=null, subordinate=null}]}
It seems like I should cut all the "null" fields here, but I can't find the way out.

If I understand correctly, you do not want to print null CyclicEmployee objects. You can check if boss and subordinates are null and then if they are skip them in toString(). Since all of them are same type, this method will work for all of them.
#Override
public String toString() {
String str = "";
str = "CyclicEmployee{" +
"age=" + age +
", name='" + name + '\'';
if (boss != null) {
str += ", boss=" + boss;
}
if (subordinate.size() != 0) {
str += ", subordinate=" + subordinate;
}
str += '}';
return str;
}

Consider using existing data structure like this or this instead.
But if you really want to use your code, you can create a NonCyclicEmployee class
private static class NonCyclicEmployee {
private int age;
private String name;
public NonCyclicEmployee(int age, String name) {
this.age=age;
this.name=name;
}
#Override
public String toString() {
return "CyclicEmployee{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
And use it in your toString() of CyclicEmployee.
private CyclicEmployee boss;
private List<CyclicEmployee> subordinate ;
private NonCyclicEmployee ncBoss;
private List<NonCyclicEmployee> ncSubordinate ;
#Override
public String toString() {
return "CyclicEmployee{" +
"age=" + age +
", name='" + name + '\'' +
", boss=" + ncBoss +
", subordinate=" + ncSubordinate +
'}';
}
And create a method addBoss() and addSubordinate() to create both bosses (boss and ncBoss) and both subordinate at the same time.

Related

Using the Adress class in Student class with toString in java

I'm trying to create a program where you can add Student info with their address. Therefor I have created 2 classes, class Student and class Adres (dutch for address).
In the Adres class I have the address data and in Student the student data.
But i'm getting stuck at creating a constructor for my Student class because when you add a new student the address of that student also needs to be added.
And I'm trying to use the toString method in Student, but the address needs to be returned aswell.
So I have 3 questions:
How to set up the constructor for class Student where the address also needs to be added
how to set up the toString method where it also returns the "toString" from Adres.
How do I input the LocalDate and address when adding a new student. (localdate is used for the students birthday)
I'm fairly new to java and if someone could help me that would be awesome.
My Adres class:
package oop3.studenten;
public class Adres {
private String straat;
private Integer huisnr;
private String postcode;
private String plaats;
public Adres(String straat, Integer huisnr, String postcode, String plaats) {
this.straat = straat;
this.huisnr = huisnr;
this.postcode = postcode;
this.plaats = plaats;
}
public String toString() {
return straat + "" + huisnr + "," + postcode + "" + plaats;
}
// Using regex to check if postcode is valid
public static boolean checkPostcode(String postCode) {
return postCode.matches("[1-9][0-9]{3}[a-zA-Z]{2}");
}
}
My Student class:
package oop3.studenten;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class Student {
private Integer studentnr; //StudentId
private String voornaam; //Firstname
private String achternaam; //Lastname
private LocalDate geboortedatum; //Birthday
private Adres adres; //address
// constructor for Student
public Student(Integer studentnr, String voornaam, String achternaam, LocalDate geboortedatum, Adres adres){
this.studentnr = studentnr;
this.voornaam = voornaam;
this.achternaam = achternaam;
this.geboortedatum = geboortedatum;
this.adres = new Adres();
}
// toString method for Student
#Override
public String toString() {
return "Student{" +
"studentnr=" + studentnr +
", voornaam='" + voornaam + '\'' +
", achternaam='" + achternaam + '\'' +
", geboortedatum=" + korteGeboortedatum(geboortedatum) +
", adres=" + adres +
'}';
}
// method to return birthday (geboortedatum) in day month year format
public String korteGeboortedatum(LocalDate gebdatum ){
return gebdatum.format(DateTimeFormatter.ofPattern("dd-MM-yyyy"));
}
}
And my main class where I try to add a student
package oop3.studenten;
public class Main {
public static void main(String[] args) {
Student student = new Student(500739074, "Ronny", "Giezen", 22111997, "?"
);
}
}
Thanks in advance
Student constructor:
public Student(Integer studentnr, String voornaam, String achternaam, LocalDate geboortedatum, Adres adres){
this.studentnr = studentnr;
this.voornaam = voornaam;
this.achternaam = achternaam;
this.geboortedatum = geboortedatum;
this.adres = adres; //if you do "new Adres();" a completely empty adres instance would be created, instead you want to use the one passed as parameter
}
// toString method for Student
#Override
public String toString() {
return "Student{" +
"studentnr=" + studentnr +
", voornaam='" + voornaam + '\'' +
", achternaam='" + achternaam + '\'' +
", geboortedatum=" + korteGeboortedatum(geboortedatum) +
", adres=" + adres.toString() +
'}'; // ^^ calling the .toString()-method of adres and appending it to the rest
}
Main Class:
public static void main(String[] args) {
Adres adres = new Adres("Mainstreet", 5, "48484", "Amsterdam"); //creating the adress
LocalDate birthday = LocalDate.of(2017, 1, 13); //creating the birthday-localdate
Student student = new Student(500739074, "Ronny", "Giezen", birthday, adres); //passing the birthday & adres to the student-constructor
}
In your Main Class you can create the Address Object first and create the Student after.
In the Student-Constructor you will be able to pass the address object.
public static void main(String[] args) {
Address studentAddress = new Address("straat", 1, "postcode", "plaats")
Student student = new Student(500739074, "Ronny", "Giezen", null, studentAddress);
}
Question
You just call your toString()-Method created in your Address-Class
#Override
public String toString() {
return "Student{" +
"studentnr=" + studentnr +
", voornaam='" + voornaam + '\'' +
", achternaam='" + achternaam + '\'' +
", geboortedatum=" + korteGeboortedatum(geboortedatum) +
", adres=" + adres.toString() +
'}';
}
Question
You have to create a LocalDate Object and pass it in the constructor
public static void main(String[] args) {
Address studentAddress = new Address("straat", 1, "postcode", "plaats")
Student student = new Student(500739074, "Ronny", "Giezen", new LocalDate.now(), studentAddress); // for example
}

why i cant print using 'tostring' ? how can i print arry that includes classes in it?

public String toString() { // this is in anther class
String Cubes = "+";
for(int i = 0; i < CubesStack.length;) {
Cubes = Cubes + CubesStack[i].toString();
}
return "maxCubes = " + this.maxCubes + "\n" +
"currentCubes = " + this.currentCubes + "\n" +
"CubesStack = " + Cubes;
}
public class MainMain { // this is main
public static void main(String[] args) {
// TODO Auto-generated method stub
cubesTower tower1 = new cubesTower(20);
System.out.println(tower1.toString());
}
}
why i cant print using 'tostring' ? how can i print arry that includes classes in it ? thank you very much .
Your object should implement toString() method other wise it will take default implementation of toString() from java.lang.Object.
java.lang.Object default implementation is
public String toString() {
return getClass().getName() + "#" + Integer.toHexString(hashCode());
}
So you have to override the toString() to get your own values
For Example:
Here I have Name class which will have two properties
class Name {
private String firstName;
private String lastName;
public Name(String firstName, String lastName) {
super();
this.firstName = firstName;
this.lastName = lastName;
}
#Override
public String toString() {
return "Name [firstName=" + firstName + ", lastName=" + lastName + "]";
}
}
public static void main(String[] args) {
Name[] names = new Name[]{new Name("john","doe"),new Name("john1","doe1")};
System.out.println(names[0]);
}
If I don't override toString() then output: com.stackovflow.problems.Name#33909752
After implementing toString() output: Name [firstName=john, lastName=doe]

Why isn't my derived class method calling the overriden method from base class despite using the super keyword?

I have three classes named Human.java, Superhero.java and Run.java. The Superhero extends Human and the method introduce() is overridden in Superhero with call to parent class's introduce(). But, When I'm making a superhero object and calling the introduce method, it doesn't print the base class method's returned value. What's wrong? Thanks in advance.
Human.java
public class Human implements Comparable<Human> {
private int age;
private String name;
public Human(String givenName, int age) {
this.name = givenName;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String introduce() {
return "Hey! I'm " + name + " and I'm " + age + " years old.";
}
#Override
public int compareTo(Human H1) {
if(this.getAge() > H1.getAge())
return 1;
else if (this.getAge() < H1.getAge())
return -1;
else
return 0;
}
}
Superhero.java
public class Superhero extends Human {
private String alterEgo;
private int age;
private String name;
public Superhero(String givenName, int age, String alterEgo) {
super(givenName, age);
this.alterEgo = alterEgo;
}
public String getAlterEgo() {
return alterEgo;
}
#Override
public String introduce(){
super.introduce();
return "I am also known as " + alterEgo + "!";
}
}
Run.java
public class Run {
public static void main(String[] args) {
Superhero superhero = new Superhero("Bruce", 26, "Batman");
System.out.println(superhero.introduce());
}
}
#Override
public String introduce(){
super.introduce();
return "I am also known as " + alterEgo + "!";
}
The introduce method of the SuperHero class is dumping the String returned by the call to the base method & returning "I am also known as " + alterEgo + "!" instead.
You need to return the result of the concatenation of the String returned by the base class's implementation + the SuperHero-specific string:
#Override
public String introduce(){
return super.introduce() + "I am also known as " + alterEgo + "!";
}
1st: You are calling super.introduce() which returns a string but you are not doing anything with that string. You need to assign it to a variable and add it to your return statement for it to be visible.
2nd: I recommend you change the introduce() method to toString() since that way you can get the string by just writing:
System.out.println(superhero);
Here is what you need to do to return the "Hey! I'm " + name + " and I'm " + age + " years old." part as well:
#Override
public String toString(){
return super.introduce() + "\n" + "I am also known as" + alterEgo + "!";
}
Personally i prefer implementing my toString() methods like this.
#Override
public String toString(){
String string = super.toString();
string = string + "\n";
string = string + "I am also known as";
string = string + alterEgo;
string = string + "!";
return string;
}
The reason your method does not print what's returned from the superclass is that your overriding method drops the return value. Generally, this is OK, so Java does not warn you about that. However, in this case you want to use the return value, so you should not ignore the result of calling the super.introduce() method.
Your method should take that value, and append to it, like this:
#Override
public String introduce(){
return super.introduce() + "\n"
+ "I am also known as " + alterEgo + "!";
}

How to print contents of a class object by field?

I have a POJO class, Location that is used to map a JSON file to a class using Jackson. The current implementation can print out every Location object in the class by calling,Location's toString() but I'm wondering how I can print for example, just the location with id= "2", which would be name="Desert"
At the moment, I use a toString method like this to print all the contents of Location:
public String toString() {
return "Location [location=" + Arrays.toString(location) + ", id=" + id
+ ", description=" + description + ", weight=" + weight
+ ", name=" + name + ", exit=" + Arrays.toString(exit)
+"]";
}
Does anyone know how I can print specific locations within the Location object based on a field id?
This is an example of what is stored in the Location class when I call toString() on it:
http://hastebin.com/eruxateduz.vhdl
An example of one of the Locations within the Location object:
[Location [location=null, id=1, description=You are in the city of Tiberius. You see a long street with high buildings and a castle.You see an exit to the south., weight=100, name=Tiberius, exit=[Exit [title=Desert, direction=South]]]
This is the POJO location class I use to map the JSON fields to the class:
public class Location {
private Location[] location;
private int id;
private String description;
private String weight;
private String name;
private Exit[] exit;
private boolean visited = false;
private boolean goalLocation;
private int approximateDistanceFromGoal = 0;
private Location parent;
public Location[] getLocation() {
return location;
}
public void setLocation(Location[] location) {
this.location = location;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription ()
{
return description;
}
public void setDescription (String description)
{
this.description = description;
}
public String getWeight() {
return weight;
}
public void setWeight(String weight) {
this.weight = weight;
}
public String getName ()
{
return name;
}
public void setName (String name)
{
this.name = name;
}
public Exit[] getExit() {
return exit;
}
public void setExit(Exit[] exit) {
this.exit = exit;
}
public boolean isVisited() {
return visited;
}
public void setVisited(boolean visited) {
this.visited = visited;
}
public boolean isGoalLocation() {
return goalLocation;
}
public void setGoalLocation(boolean goalLocation) {
this.goalLocation = goalLocation;
}
public int getApproximateDistanceFromGoal() {
return approximateDistanceFromGoal;
}
public void setApproximateDistanceFromGoal(int approximateDistanceFromGoal) {
this.approximateDistanceFromGoal = approximateDistanceFromGoal;
}
public Location getParent() {
return parent;
}
public void setParent(Location parent) {
this.parent = parent;
}
#Override
public String toString() {
return "Location [location=" + Arrays.toString(location) + ", id=" + id
+ ", description=" + description + ", weight=" + weight
+ ", name=" + name + ", exit=" + Arrays.toString(exit)
+"]";
}
}
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
You need the above dependency to define predicate unless you want to do it on your own.
public class Location {
private int id;
// more stuff here
private Predicate<Integer> filter;
public Location() {
this.filter = TruePredicate.INSTANCE;
}
public Location(int idFilter) {
this.filter = new EqualPrediate(idFilter);
}
public String toString() {
StringBuilder buffer = new StringBuilder();
if(filter.apply(this.id)) {
buffer.append("Location [location=" + Arrays.toString(location) + ", id=" + id
+ ", description=" + description + ", weight=" + weight
+ ", name=" + name + ", exit=" + Arrays.toString(exit)
+"]");
}
return buffer.toString();
}
}
This code is a simplified Visitor Pattern where the
'Visitor' -> your predicate
'this' -> 'this.id'
This works because your toString() is invoking the toString() of the nested Location objects which also have their predicates for filtering set.
If you aren't in control of their construction where you can propogate the filter then you can take this approach:
public String toString() {
StringBuilder buffer = new StringBuilder();
int i = 0;
for(Location l = this; i < locations.length; l = locations[i++])
if(filter.apply(l.id) {
buffer.append("Location [location=" + Arrays.toString(location) + ", id=" + id
+ ", description=" + description + ", weight=" + weight
+ ", name=" + name + ", exit=" + Arrays.toString(exit)
+"]");
}
return buffer.toString();
}
Stream.of(location).filters(l -> l.getId() == 2).foreach(System.out::println);
Does that work?
You can have a try with gson, which inputs a object and outputs a JSON or in the opposite side.
After u make the object a JSONObject, you can ergodic the JSON in order to ergodic object.

Trouble with inner class, toString confliction

Based on one of the Head First books examples I'm having a little trouble in which the toString method is causing issues with my student and they're uni and home address being outputted correctly. All i'm trying to do is output if a students uni address is empty use his home address else use the uni one.
But my test data looks like the following
John John72 Nottingham Drive
John72 Nottingham Drive
public class Test {
public static void main(String[] args){
Student student1 = new Student("John", 19, "Walkers Way");
student1.setUniAddress(72, "Nottingham Drive");
System.out.print(student1.toString());
}
}
Other Class
public class Student {
private String name;
private Address homeAddress, uniAddress;
public Student(String name, int houseNumber, String homeStreet){
this.name = name;
this.homeAddress = new Address(houseNumber, homeStreet);
}
public String getName() { return this.name; }
public Address getHomeAddress(){
if(this.uniAddress == null){
return this.homeAddress;
}else{
return getUniAddress();//this.uniAddress;
}
}
public Address getUniAddress() { return this.uniAddress; }
public void setUniAddress(int number, String add){
Address address = new Address(number, add);
uniAddress = address;
}
#Override
public String toString(){
return getName() + " " + getHomeAddress() + " " + getUniAddress() + "\n";
}
public class Address{
private int number;
private String street;
public Address(int no, String street){
this.number = no;
this.street = street;
}
#Override
public String toString(){
return name + number + " " + street + "\n";
}
}
}
Your getHomeAddress method takes care of displaying the uni address, so this line:
return getName() + " " + getHomeAddress() + " " + getUniAddress() + "\n";
can be shortened to:
return getName() + " " + getHomeAddress() + " " + "\n";
Otherwise your getHomeAddress method will pull the uni address, then your getUniAddress method will pull the uni address again.
Also in your address toString you are pulling the person's name and you probably didn't mean to (and you might not want a newline here either since you have a newline in your other toString method).
#Override
public String toString(){
return number + " " + street;
}

Categories