14

Is there a way to make toString() include private fields of the super class? I tried adding a super.toString(), no use however.

Please see the code below

Employee.java

package test;

public class Employee {

private  String name;
private int id;
private double salary;

public Employee(String name, int id, double salary) {
    super();
    this.name = name;
    this.id = id;
    this.salary = salary;
}

public double getSalary() {
    return salary;
}

@Override
public String toString() {
    return "Employee [name=" + name + ", id=" + id + ", salary=" + salary
            + "]";
}

public static void main(String[] args) {

    Employee e=new Employee("Joe", 14, 5000);
    System.out.println(e);
    Manager m=new Manager("Bill", 23, 5000, 10);
    System.out.println(m);
    System.out.println("Employee Salary is "+e.getSalary()+"\nManager salary is "+m.getSalary());
}
}

Manager.java

package test;

public class Manager extends Employee{

private double bonus;
public Manager(String name, int id, double salary,int bonus) {
    super(name, id, salary);
    this.bonus=bonus;
}

public double getSalary()
{
    double baseSalary=super.getSalary();

    return (baseSalary+baseSalary*(bonus/100));

}

@Override
public String toString() {
    return(this.getClass().getName()+" ["+super.toString().substring((this.getClass().getSuperclass().getName().length()-3
            ), (super.toString().length())-1)+", bonus="+bonus+"]");
    //didn't work
    //super.toString();
    //return "Manager [bonus=" + bonus + "]";
}



}

Output

Employee [name=Joe, id=14, salary=5000.0]
test.Manager [name=Bill, id=23, salary=5000.0, bonus=10.0]
Employee Salary is 5000.0
Manager salary is 5500.0

That was the best i could do , to concatenate super.toString()+' a set of Strings', surely this is messy , is there some other way , even if the language spec does not allow it does eclipse have some facility to do that , NOTE: I used eclipse to generate the toString method , any way by which i can tell eclipse to include the super class fields too,

In other words can i replace this messy code

return(this.getClass().getName()+" ["+super.toString().substring((this.getClass().getSuperclass().getName().length()-3
            ), (super.toString().length())-1)+", bonus="+bonus+"]");

by getting eclipse to automate the process and generate a suitable way to do it?

Sainath S.R
  • 3,074
  • 9
  • 41
  • 72
  • 1
    The only way is to cal the getter of the private variable. You can not acces the private variable directly. – Jens Nov 26 '14 at 08:05
  • You could change their scope to protected, that way they will be visible in Manager class, but not to other classes. – Beri Nov 26 '14 at 08:06
  • Or (not recommended) use reflection to access the fields. – Magnilex Nov 26 '14 at 08:07
  • 1
    You need to do something similar to Guava's [`ToStringHelper`](https://code.google.com/p/guava-libraries/issues/detail?id=1239). I.e. have a `protected` method that returns some sort of "builder" that can be called from a subclass, added to and then returned. What you're trying to do is not only ugly but also horribly slow. – Boris the Spider Nov 26 '14 at 08:10

7 Answers7

13

If you create getters and setters in your superclass then you can acces the variables through those methods. Other possibility is to change the visibility from private to protected

first solution looks like this

Employee

public class Employee {

    private String name;
    private int id;
    private double salary;

    public Employee(String name, int id, double salary) {
        super();
        this.name = name;
        this.id = id;
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee [name=" + name + ", id=" + id + ", salary=" + salary + "]";
    }
}

Manager

public class Manager extends Employee {

    private double bonus;

    public Manager(String name, int id, double salary, int bonus) {
        super(name, id, salary);
        this.bonus = bonus;
    }

    public double getSalary() {
        double baseSalary = super.getSalary();

        return (baseSalary + baseSalary * (bonus / 100));

    }

    @Override
    public String toString() {
        return "Manager [name=" + getName() + ", id=" + getId() + ", salary=" + getSalary() + ", bonus=" + bonus + "]";
    }

}

Second one (using protected)

Employee

public class Employee {

    protected String name;
    protected int id;
    protected double salary;

    public Employee(String name, int id, double salary) {
        super();
        this.name = name;
        this.id = id;
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }



    @Override
    public String toString() {
        return "Employee [name=" + name + ", id=" + id + ", salary=" + salary + "]";
    }
}

Manager

public class Manager extends Employee {

    protected double bonus;

    public Manager(String name, int id, double salary, int bonus) {
        super(name, id, salary);
        this.bonus = bonus;
    }

    public double getSalary() {
        double baseSalary = super.getSalary();

        return (baseSalary + baseSalary * (bonus / 100));

    }

    @Override
    public String toString() {
        return "Manager [name=" + name + ", id=" + id + ", salary=" + salary + ", bonus=" + bonus + "]";
    }

}

Personally i'd use the getter/setter method but it's up to you.

EDIT: Additonal to eclipse generation of toString() in eclipse. You can't seem to generate it with getters and setter (just had a quick look, you can see some documentation here. What I did figure out is how you can edit the Code Template used when generating the toString() so it includes the toString() from the superclass.

When you enter the generate toString() dialog there is a field 'String Format' with <Default Template> next to it. when you click the edit button you can create a new Code Template. This template automatically holds the <Default Template> and should look something like this:

${object.className} [${member.name()}=${member.value}, ${otherMembers}]

only thing you'll have to add is the following at the end

[super: ${object.superToString}]

This way it'll display the toString() form the superclass

Liam de Haas
  • 1,258
  • 3
  • 18
  • 39
  • I understand that , thanks ,but can i get eclipse to automate this for me , just like how it generated the toString() , is there an option like toString with Super members etc , because i can imagine it becoming tedious if i have like 10 subclasses and have to manually code the toString for each when all i want is just to debug using the to \String by printing out to the console – Sainath S.R Nov 26 '14 at 08:23
  • @Droid Ics that wasn't your original question, but i'll see if I can work it out for you – Liam de Haas Nov 26 '14 at 08:25
  • Thanks and its in the last line – Sainath S.R Nov 26 '14 at 08:26
  • For anyone who downvotes: Could you please leave a comment and explain why, so I can improve my answer. – Liam de Haas Nov 26 '14 at 08:55
  • I dont just want to toString() form the superclass, i want to automate this whole thing..." return "Manager [name=" + name + ", id=" + id + ", salary=" + salary + ", bonus=" + bonus + "]"; As how to create a template that appends the subclass fields and the superclass ones in a proper className[Superfeilds ,subfields] format that i can use by selection,source ->generate toString() including super() sort of .... – Sainath S.R Nov 26 '14 at 09:03
  • @DroidIcs as stated in the answer, that is not possible. – Liam de Haas Nov 26 '14 at 09:04
  • I know this is old, but the problem with Solution 1 and Solution 2 are that anybody adding a field to Employee has to also edit toString() in Manager and any other implementation of Manager. This may be obvious to the individual who implemented Employee's toString(), but not to another developer. Another problem is if the decision was made to do something like split name into multiple fields (first, middle, last) all of the implementations of Employee will no longer compile. – tuckerpm Apr 29 '19 at 12:39
11

You can let eclipse generate it, however it would not look like you want it.

Eclipse generate toString() dialog

which creates this code:

public String toString() {
    return "Manager [bonus=" + bonus + ", toString()=" + super.toString() + "]";
}

which would print this:

Manager [bonus=10.0, toString()=Employee [name=Bill, id=23, salary=5000.0]]

That's the most you can make eclipse generate for you.

You can clean it up a little so it would look like this

public String toString() {
    return "Manager [bonus=" + bonus +  "] is a " + super.toString();
}

which would print

Manager [bonus=10.0] is a Employee [name=Bill, id=23, salary=5000.0]


However, your custom solution works as well. So why not use it?

You can clean it up a little like this:

@Override
public String toString() {
    return "Manager [" + superFieldsFromToString() + ", bonus=" + bonus + "]";
}

private String superFieldsFromToString() {
    String superToString = super.toString();
    int superClassNameLength = getClass().getSuperclass().getSimpleName().length();
    int fieldStartIdx = superClassNameLength + 2; // + 2 removes " ["
    int fieldEndIdx = superToString.length() - 1; // - 1 removes "]"
    return superToString.substring(fieldStartIdx , fieldEndIdx);
}

which outputs

Manager [name=Bill, id=23, salary=5000.0, bonus=10.0]

The only other options, as others have mentioned, are to use reflection to access the private fields, make the fields protected or create public getters.

I would not advice to do any of this, as your class design should not be defined by debug output.

Timo Türschmann
  • 4,388
  • 1
  • 22
  • 30
  • Thanks ! , Manager [bonus=10.0] is a Employee [name=Bill, id=23, salary=5000.0] is very useful and nicer to look at while debugging – Sainath S.R Nov 26 '14 at 09:07
1

No. Because if you could directly access private fields of super class, those fields would not be private.

Wayne Huang
  • 495
  • 6
  • 17
1

There is something that doesn't make sense:

  1. All your fields are either primitives and immutable thus you can safely publish (For Concurenecy purposes) them using getters.

  2. If you have in mind making salary, id, bonus private because these should not be known(and thus not provide getters), then why provide a toString that shows this secrete information. If the toString is just for visual testing purposes, then consider making them protected and then put them private again when you have successfully tested your class.

Otherwise, there is something wrong in the structure of your classes and you are just trying to do some hacks in the language.

javaHunter
  • 1,097
  • 6
  • 9
0

The possible solution is to make a protected getters for fields in case you don't want to make fields itself protected.

retroq
  • 572
  • 2
  • 7
0

Best Solution

You can call the getters for the super class instance variables and put the values on your toString(). You could even be sneaky and make the getters protected so that only the child classes can view the variable's value.


Lazy Solution

Make the fields protected which might be a bad idea depending on how your super class is designed.


Bad Idea

You could also use reflection, but I wouldn't do that.

Reflection was created for a specific purpose, to discover the functionality of a class that was unknown at compile time, similar to what the dlopen and dlsym functions do in C. Any use outside of that should be heavily scrutinized. (https://softwareengineering.stackexchange.com/questions/193526/is-it-a-bad-habit-to-overuse-reflection)

Community
  • 1
  • 1
apxcode
  • 7,696
  • 7
  • 30
  • 41
  • I disagree with the best solution - see my comment on the question itself. I think exposing getters that don't need to be exposed isn't the best thing to to. The best thing to do is design the `toString` method for extensibility. For example, build a `Map` in a protected method. – Boris the Spider Nov 26 '14 at 08:32
  • @BoristheSpider that might be optimal, yet it does sound like overkill for the question above. You should definitely post it, you shall have my upvote indeed. – apxcode Nov 26 '14 at 08:57
0

If you can't change the code of the super class (change scope of members or add a getter), then you can use the Java reflection API to access a private field:

Manager managerObject = new managerObject():
Employee e = (Employee) managerObject;
Field f = e.getClass().getDeclaredField("salary");
f.setAccessible(true);
double sal = (double) f.get(e);
Community
  • 1
  • 1
Udo Klimaschewski
  • 5,150
  • 1
  • 28
  • 41