如何在 Java 中正确覆盖 toString()?

Posted

技术标签:

【中文标题】如何在 Java 中正确覆盖 toString()?【英文标题】:How to override toString() properly in Java? 【发布时间】:2012-05-30 20:05:48 【问题描述】:

听起来有点愚蠢,但我的toString() 方法需要帮助,这很烦人。 我尝试在网上查找,因为toString 是它搞砸的地方并且“没有找到 Kid 构造函数 #2”,即使它在那里,我什至会做其他事情但它不起作用。 好的,这很多,所以这是我的代码:

import java.util.*; 
   class Kid   
      String name; 
      double height; 
      GregorianCalendar bDay; 

      public Kid ()  
         this.name = "HEAD";
         this.height = 1; 
         this.bDay = new GregorianCalendar(1111,1,1); 
       

      public Kid (String n, double h, String date) 
      // method that toString() can't find somehow
         StringTokenizer st = new StringTokenizer(date, "/", true);
         n = this.name;
         h = this.height;
       

      public String toString()  
         return Kid(this.name, this.height, this.bDay);
       
    //end class 

好的,所以我上面的 toString(我知道,我的第三个参数是关闭的,应该是一个字符串)是关闭的。如果我为第三件事硬编码一个值,它会变得混乱并说它找不到这个(上面)。那么我怎样才能得到日期并将其分解呢?

下面是调用这个的类

class Driver    
   public static void main (String[] args)    
      Kid kid1 = new Kid("Lexie", 2.6, "11/5/2009");   
      System.out.println(kid1.toString());
    //end main method 
 //end class  

我尝试研究多个构造函数,但确实没有帮助。 我尝试研究 toString() 方法,并尝试使用我之前创建的以前的 toString() 方法逻辑,但这是全新的,所以它从来没有工作过。

帮助?

【问题讨论】:

请发布实际编译的代码。您的 toString() 方法不返回字符串,并且您不能调用这样的构造函数。目前还不清楚你甚至想要达到什么目的。请阅读tinyurl.com/so-hints 并澄清您的问题。 Spring 的 ToStringCreator (github.com/SpringSource/spring-framework/tree/master/…) 做得非常好。 仅供参考,非常麻烦的旧日期时间类,例如 java.util.Datejava.util.GregorianCalendarjava.text.SimpleDateFormat 现在是 legacy,被 Java 8 及更高版本中内置的 java.time 类所取代.见Tutorial by Oracle。 【参考方案1】:

toString 应该返回一个String

public String toString()  
    return "Name: '" + this.name + "', Height: '" + this.height + "', Birthday: '" + this.bDay + "'";
 

我建议您利用 IDE 的功能来生成 toString 方法。不要手动编码。

例如,Eclipse 可以这样做,只需右键单击源代码并选择Source > Generate toString

【讨论】:

这不是一个好习惯,因为您正在使用字符串连接。 StringBuilder 在这种情况下是更好的选择。 @Amit Java 编译器隐式使用StringBuilder。除非你在循环中使用它是没有问题的。 在 Netbeans 中;它在Source -> Insert Code... 下然后选择toString【参考方案2】:

Java toString() 方法

如果要将任何对象表示为字符串,toString() 方法就应运而生了。

toString() 方法返回对象的字符串表示形式。

如果您打印任何对象,java 编译器会在内部调用该对象的 toString() 方法。所以重写 toString() 方法,返回所需的输出,它可以是对象的状态等。取决于你的实现。

Java toString()方法的优点

通过重写 Object 类的 toString() 方法,我们可以返回对象的值,所以我们不需要写太多代码。

没有 toString() 方法的输出

class Student  
 int id;  
 String name;  
 String address;  

 Student(int id, String name, String address)  
 this.id=id;  
 this.name=name;  
 this.address=address;  
   

 public static void main(String args[])  
   Student s1=new Student(100,”Joe”,”success”);  
   Student s2=new Student(50,”Jeff”,”fail”);  

   System.out.println(s1);//compiler writes here s1.toString()  
   System.out.println(s2);//compiler writes here s2.toString()  
   
  

Output:Student@2kaa9dc
       Student@4bbc148

您可以在上面的示例 #1 中看到。打印 s1 和 s2 打印对象的哈希码值,但我想打印这些对象的值。由于 java 编译器内部调用 toString() 方法,覆盖该方法将返回指定的值。让我们通过下面给出的示例来理解它:

Example#2

Output with overriding toString() method

class Student  
 int id;  
 String name;  
 String address;  

 Student(int id, String name, String address)  
 this.id=id;  
 this.name=name;  
 this.address=address;  
   

//overriding the toString() method  
public String toString() 
  return id+" "+name+" "+address;  
   
 public static void main(String args[])  
   Student s1=new Student(100,”Joe”,”success”);  
   Student s2=new Student(50,”Jeff”,”fail”);  

   System.out.println(s1);//compiler writes here s1.toString()  
   System.out.println(s2);//compiler writes here s2.toString()  
   
 

Output:100 Joe success
       50 Jeff fail

注意 toString() 主要与 Java 中的多态性概念有关。 在 Eclipse 中,尝试单击 toString() 并右键单击它。然后,单击 Open Declaration 并查看超类 toString() 的来源。

【讨论】:

【参考方案3】:

正如其他人解释的那样,toString 不是实例化您的类的地方。相反,toString 方法旨在构建一个表示类实例值的字符串,至少报告存储在该对象中的最重要的数据字段。在大多数情况下,toString 用于调试和记录,而不是用于您的业务逻辑。

要生成表示对象值的文本以显示给用户,请添加另一个方法。人们经常将方法命名为getDisplayName。例如,DayOfWeek::getDisplayNameMonth::getDisplayName

StringJoiner

从 Java 8 及更高版本开始,实现toString 的最现代方式将使用StringJoiner 类。正如文档所说:

StringJoiner 用于构造由分隔符分隔的字符序列,并且可以选择以提供的前缀开头并以提供的后缀结尾。

这样使用:

@Override
public String toString ()

    return new StringJoiner(                           // In Java 8 and later, StringJoiner is used to construct a sequence of characters separated by a delimiter and optionally starting with a supplied prefix and ending with a supplied suffix.
                " | " ,                                // Delimiter
                Person.class.getSimpleName() + "[ " ,  // Prefix
                " ]"                                   // Suffix
            )
            .add( "name=" + name )                     // Append
            .add( "phone=" + phone )                   // Append
            .toString();                               // Convert entire sequence to a single `String` object.

人[名称=爱丽丝|电话=555.867.5309]

record

Java 16 带来了一种新方法来简要定义一个主要目的是透明且不可变地通信数据的类:record。

您仅通过列出每个成员字段的类型和名称来定义记录。编译器隐式创建构造函数、getter、equals & hashCodetoString

toString的默认实现

toString 的默认实现包括每个成员字段。

public Kid  String name , double height , LocalDate birthDate )  

像任何其他对象一样实例化。

Kid alice = new Kid( "Alice" , 6.1d , LocalDate.of( 2019 , Month.April , 23 ) ) ;
String output = alice.toString() ;

您可以选择用自己的方式覆盖默认实现。考虑到记录作为简单数据载体的目的,通常不需要覆盖。

【讨论】:

这样更优雅【参考方案4】:

您可以在 toString() 中创建新对象。 使用

return "Name = " + this.name +" height= " + this.height;

而不是

return Kid(this.name, this.height, this.bDay);

您可以根据需要更改返回字符串。还有其他方法可以存储日期而不是日历。

【讨论】:

【参考方案5】:

你不能像普通方法一样调用构造函数,你只能用new调用它来创建一个新对象:

Kid newKid = new Kid(this.name, this.height, this.bDay);

但是从你的 toString() 方法构造一个新对象并不是你想要做的。

【讨论】:

【参考方案6】:

以下代码是一个示例。基于相同的问题,而不是使用基于IDE的转换,有没有更快的方法来实现,以便将来发生更改,我们不需要一遍又一遍地修改值?

@Override
    public String toString() 
        return "ContractDTO" +
                "contractId='" + contractId + '\'' +
                ", contractTemplateId='" + contractTemplateId + '\'' +
                '';
    

【讨论】:

【参考方案7】:

如果您对单元测试感兴趣,那么您可以声明一个公共的“ToStringTemplate”,然后您可以对您的 toString 进行单元测试。即使你不对其进行单元测试,我认为它“更干净”并使用 String.format。

public class Kid 

    public static final String ToStringTemplate = "KidName='%1s', Height='%2s', GregCalendar='%3s'";

    private String kidName;
    private double height;
    private GregorianCalendar gregCalendar;

    public String getKidName() 
        return kidName;
    

    public void setKidName(String kidName) 
        this.kidName = kidName;
    

    public double getHeight() 
        return height;
    

    public void setHeight(double height) 
        this.height = height;
    

    public GregorianCalendar getGregCalendar() 
        return gregCalendar;
    

    public void setGregCalendar(GregorianCalendar gregCalendar) 
        this.gregCalendar = gregCalendar;
    

    public String toString()  
        return String.format(ToStringTemplate, this.getKidName(), this.getHeight(), this.getGregCalendar());
     

现在您可以通过创建 Kid、设置属性以及在 ToStringTemplate 上执行自己的 string.format 并进行比较来进行单元测试。

使 ToStringTemplate 成为静态最终意味着事实的“一个版本”,而不是在单元测试中拥有模板的“副本”。

【讨论】:

【参考方案8】:

其实你需要返回这样的东西,因为 toString 必须返回一个字符串

public String toString() 
 return "Name :" + this.name + "whatever :" + this.whatever + "";

你实际上在构造函数中做错了,你将用户设置的变量设置为名称,而你需要做相反的事情。 你不应该做的事

n = this.name

你应该怎么做

this.name = n

希望对你有帮助,谢谢

【讨论】:

【参考方案9】:

我们甚至可以通过在类中创建一个新的 String 对象并在构造函数中为其分配我们想要的任何内容并在被覆盖的 toString 方法中返回它来编写这样的方法

public class Student  
 int id;  
 String name;  
 String address;  
 String details;
 Student(int id, String name, String address)  
 this.id=id;  
 this.name=name;  
 this.address=address;  
 this.details=id+"  "+name+"  "+address;  
   

//overriding the toString() method  
public String toString() 
  return details;  
   
 public static void main(String args[])  
   Student s1=new Student(100,"Joe","success");  
   Student s2=new Student(50,"Jeff","fail");  

   System.out.println(s1);//compiler writes here s1.toString()  
   System.out.println(s2);//compiler writes here s2.toString()  
   

【讨论】:

【参考方案10】:

简洁明了的方法是使用 Lombok 注释。它有@ToString 注释,它将生成toString() 方法的实现。默认情况下,它将按顺序打印您的班级名称以及每个字段,以逗号分隔。 您可以通过将参数传递给注释来轻松自定义输出,例如:

@ToString(of = "name", "lastName")

相当于纯Java:

public String toString() 
        return "Person(name=" + this.name + ", lastName=" + this.experienceInYears + ")";
    

【讨论】:

【参考方案11】:

如果您只是使用toString() 来调试DTO,您可以使用以下内容自动生成人类可读的输出:

import com.fasterxml.jackson.databind.ObjectMapper;
...
public String toString() 
    try  return new ObjectMapper().writeValueAsString(this); 
    catch (Exception e)  return "ObjectMapper failed"; 


但是,如果 DTO 可能包含 PII(不应在日志中捕获),这不适用于生产部署。

【讨论】:

【参考方案12】:

总是有简单的方法:右键单击 > 生成 > toString() > 选择您想要的模板。

【讨论】:

【参考方案13】:

我认为最好的方法是使用 google gson 库:

        @Override
public String toString() 
    return new GsonBuilder().setPrettyPrinting().create().toJson(this);

或者apache commons lang反射方式

【讨论】:

【参考方案14】:

    如果您使用记事本: 那么

    public String toString()
    
     return ""; ---now here you can use variables which you have created for your class
    
    
    

    如果您使用的是 Eclipse IDE,那么 按

    -alt +shift +s 
    

    -单击覆盖 toString 方法,您将获得选择要选择的变量类型的选项。

【讨论】:

以上是关于如何在 Java 中正确覆盖 toString()?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 F# 中覆盖复合类型中的 ToString?

Java 对象 始终要覆盖toString

Java:Effective java学习笔记之 始终要覆盖toString方法

3覆盖toString方法

覆盖 .ToString()

intellij idea中怎么覆盖tostring(),hashcode(),equals()?