【问题标题】:How make toString() method return Super Class private fields also along with its instance fields?如何使 toString() 方法返回超类私有字段及其实例字段?
【发布时间】:2015-01-24 11:22:38
【问题描述】:

有没有办法让toString() 包含超级class 的私​​有字段?我尝试添加super.toString(),但是没有用。

请看下面的代码

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 + "]";
}



}

输出

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

这是我能做的最好的事情,连接super.toString()+'一组字符串',这肯定很乱,有没有其他方法,即使语言规范不允许它确实 eclipse 有一些设施这样做,注意:我使用 eclipse 来生成 toString 方法,我可以通过任何方式告诉 eclipse 也包含超类字段,

换句话说,我可以替换这个乱码

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

通过让 eclipse 自动化该过程并生成合适的方式来完成它?

【问题讨论】:

  • 唯一的办法就是调用私有变量的getter。您不能直接访问私有变量。
  • 您可以将它们的范围更改为受保护,这样它们将在 Manager 类中可见,但对其他类不可见。
  • 或者(不推荐)使用反射来访问字段。
  • 你需要做一些类似于 Guava 的ToStringHelper 的事情。 IE。有一个protected 方法,它返回某种“构建器”,可以从子类调用,添加到然后返回。你想要做的不仅丑陋而且非常缓慢。

标签: java eclipse inheritance tostring auto-generate


【解决方案1】:

如果您在超类中创建 getter 和 setter,那么您可以通过这些方法访问变量。其他可能性是将可见性从私有更改为受保护

第一个解决方案如下所示

员工

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 + "]";
    }
}

经理

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 + "]";
    }

}

第二个(使用受保护的)

员工

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 + "]";
    }
}

经理

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 + "]";
    }

}

我个人会使用 getter/setter 方法,但这取决于你。

编辑:在 Eclipse 中生成 toString() 的附加功能。 您似乎无法使用 getter 和 setter 生成它(只是快速浏览一下,您可以看到一些文档 here。 我想出的是如何编辑生成 toString() 时使用的代码模板,使其包含超类中的 toString()

当您进入生成 toString() 对话框时,会出现一个“字符串格式”字段,旁边有 <Default Template>。当您单击编辑按钮时,您可以创建一个新的代码模板。 这个模板自动保存<Default Template>,看起来应该是这样的:

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

你只需要在最后添加以下内容

[super: ${object.superToString}]

这样它将显示超类的toString()

【讨论】:

  • 我明白,谢谢,但我可以让 eclipse 为我自动执行此操作,就像它如何生成 toString() 一样,是否有类似 toString 和 Super 成员等的选项,因为我可以想象如果我有 10 个子类并且必须为每个子类手动编码 toString,而我想要的只是通过打印到控制台来使用 to \String 进行调试,这将变得乏味
  • @Droid Ics 这不是你最初的问题,但我看看能不能帮你解决
  • 谢谢,在最后一行
  • 对于任何不赞成投票的人:您能否发表评论并解释原因,以便我改进答案。
  • 我不只是想 toString() 形成超类,我想自动化这整个事情..." return "Manager [name=" + name + ", id=" + id + " ,salary=" +salary + ", bonus=" + bonus + "]"; 如何创建一个模板,以适当的 className[Superfeilds ,subfields] 格式附加子类字段和超类字段,我可以通过选择使用,source -> 生成 toString() 包括 super() 之类的 ....
【解决方案2】:

您可以让 eclipse 生成它,但它看起来不像您想要的那样。

创建此代码:

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

会打印这个:

经理 [bonus=10.0, toString()=Employee [name=Bill, id=23, Salary=5000.0]]

这是你可以让 eclipse 为你生成的最多的东西。

你可以把它清理一下,让它看起来像这样

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

会打印出来

经理 [bonus=10.0] 是员工 [name=Bill,id=23,salary=5000.0]


不过,您的自定义解决方案也可以。那为什么不使用呢?

你可以像这样清理它:

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

哪个输出

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

正如其他人所提到的,唯一的其他选择是使用反射来访问私有字段、使字段受到保护或创建公共 getter。

我不建议这样做,因为您的类设计不应由调试输出定义。

【讨论】:

  • 谢谢! , Manager [bonus=10.0] is a Employee [name=Bill, id=23,salary=5000.0] 在调试时非常有用且更好看
【解决方案3】:

没有。 因为如果你可以直接访问超类的私有字段,这些字段就不是私有的了。

【讨论】:

    【解决方案4】:

    有些东西说不通:

    1. 您的所有字段都是原语和不可变的,因此您可以使用 getter 安全地发布(出于 Concurenecy 目的)它们。

    2. 1234563如果 toString 仅用于视觉测试目的,则考虑将它们设置为受保护,然后在成功测试类后再次将它们设为私有。

    否则,你的类结构有问题,你只是想在语言中做一些 hack。

    【讨论】:

      【解决方案5】:

      可能的解决方案是为字段创建受保护的 getter,以防您不想使字段本身受到保护。

      【讨论】:

        【解决方案6】:

        最佳解决方案

        您可以为超类实例变量调用getter,并将值放在您的toString() 上。你甚至可以偷偷摸摸地把 getter 设为protected,这样只有子类才能查看变量的值。


        懒惰的解决方案

        创建字段protected,这可能不是一个好主意,具体取决于您的超类的设计方式。


        坏主意

        你也可以使用反射,但我不会那样做。

        反射是为特定目的而创建的,以发现 在编译时未知的类的功能,类似于 dlopen 和 dlsym 函数在 C 中的作用。除此之外的任何用途 应该严格审查。 (https://softwareengineering.stackexchange.com/questions/193526/is-it-a-bad-habit-to-overuse-reflection)

        【讨论】:

        • 我不同意最佳解决方案 - 请参阅我对问题本身的评论。我认为暴露不需要暴露的吸气剂并不是最好的选择。最好的办法是设计toString 方法以实现可扩展性。例如,在受保护的方法中构建Map
        • @BoristheSpider 可能是最佳选择,但对于上述问题来说,这听起来有点矫枉过正。你绝对应该发布它,你确实会得到我的支持。
        【解决方案7】:

        如果不能更改超类的代码(更改成员范围或添加getter),则可以使用Java反射API访问私有字段:

        Manager managerObject = new managerObject():
        Employee e = (Employee) managerObject;
        Field f = e.getClass().getDeclaredField("salary");
        f.setAccessible(true);
        double sal = (double) f.get(e);
        

        【讨论】:

          猜你喜欢
          • 2011-12-06
          • 2012-01-11
          • 2011-08-20
          • 1970-01-01
          • 2013-09-01
          • 2013-04-05
          • 1970-01-01
          • 2013-02-26
          • 1970-01-01
          相关资源
          最近更新 更多