toString
hat ein entwicklerfreundliches Format zum Debuggen.formatTo
hat aus Benutzersicht ein leicht verständliches Format.Edson Yanagas "Revisiting Effective Java in 2018" Sitzung beim JJUG CCC 2018 fallen Ich habe versucht, die Geschichte über formatTo zu organisieren, die mich beeindruckt hat.
Die Sitzung war sehr interessant, weil es so war, als würde man beim Live-Codieren über das Know-how von Effective Java 3rd Edition + α sprechen. In Bezug auf formatTo war ich überrascht, dass "das Paket" java.lang "so nützlich ist?". Aber selbst wenn ich mit Qiita gesucht habe, gab es fast keine Artikel, also habe ich beschlossen, es selbst zu schreiben.
Zunächst die bekannte toString-Methode.
Eine Methode, die eine Zeichenfolgendarstellung eines Objekts zurückgibt. Es wird in den folgenden Situationen aufgerufen.
Es ist in der Object-Klasse als "Klassenname +" @ "+ hexadezimale Darstellung des Hash-Werts" definiert, aber es ist nicht sehr verwaltbar und es wird empfohlen, es in der Unterklasse zu überschreiben.
Referenz Object#toString
ToString überschreiben
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + ", " + age + " years old.";
}
}
Wenn Sie "Object # toString" wie folgt überschreiben, können Sie eine formatierte Zeichenfolge ausgeben, indem Sie die Variable wie bei der Standardausgabe übergeben.
Beispiel für die Verwendung von toString
Person marty = new Person("Marty", 17);
Person doc = new Person("Doc", 65);
System.out.println(marty); // Marty, 17 years old.
System.out.println(doc); // Doc, 65 years old.
Insbesondere denke ich, dass es sehr "objektorientiert" ist zu wissen, wie das Objekt selbst stringifiziert werden sollte, unabhängig davon, wie es die spezifischen Daten enthält.
Im obigen Beispiel werden alle Debugger- und Protokollausgaben in Form von "Marty, 17 Jahre alt" ausgegeben.
Ich möchte toString
für die Benutzerausgabe verwenden, möchte aber detailliertere Informationen zum Debuggen.
Hier kommt die formatTo-Methode ins Spiel.
formatTo
Informationen zur Formatspezifikationszeichenfolge vor formatTo
Es ist ein Mechanismus, der etwas Ähnliches wie printf oder sprintf in C-Sprache tun kann.
System.out.printf("%s, %d years old.%n", "Marty", 17); // --> Marty, 17 years old.
String str = String.format("%s, %d years old.", "Marty", 17); // --> "Marty, 17 years old."Wird generiert
Da die Methode zur Angabe des Formats recht kompliziert ist, lesen Sie bitte Javadoc der Formatter-Klasse. Sie müssen jedoch vorerst nur wissen, dass das Format durch die folgenden Parameter angegeben wird.
Spezifikationsmethode für das Formatierungsformat
%[argument_index$][flags][width][.precision]conversion
Die Bedeutung von jedem ist wie folgt.
Argument_index
ist diesmal nicht relevant, wird also weggelassen.//
//Konvertierung (So konvertieren Sie das Argument. Ganzzahliger Wert ist%d ist die reelle Zahl%Geben Sie die Konvertierung entsprechend dem Typ an, z. B. f)
//
String.format("%d", 12); // ---> "12"
System.out.printf("%f", Math.PI); // ---> 3.141593
//
//Breite (Mindestanzahl von Zeichen, fehlendes Leerzeichen wird mit Leerzeichen gefüllt)
//
String.format("%5d", 12); // ---> " 12"
//
// flag (-, +, #Flags zur wörtlichen Formatierung von Optionssteuerungen usw.)
//
//Fügen Sie ein Minus hinzu, um es links auszurichten
String.format("%-5d", 12); // ---> "12 "
//Fügen Sie 0 hinzu, um Nullen anstelle von Leerzeichen zu füllen
String.format("%05d", 12); // ---> "00012"
//Am Anfang+Zeigen Sie den Code mit an
System.out.printf("%+5d", 12); // ---> "+12"
System.out.printf("%+5d", -12); // ---> "-12"
//
//Präzision (wird im Grunde genommen verwendet, um die maximale Anzahl von Zeichen zu bezeichnen)
//
//Bei einem reellen Wert die Anzahl der Stellen nach dem Dezimalpunkt (in diesem Fall scheint dies als "die maximale Anzahl der Zeichen nach dem Dezimalpunkt" behandelt zu werden).
System.out.printf("%4.2f", Math.PI); // ---> 3.14
Zum Beispiel hat% -4.2f die folgenden Werte.
conversion | flag | width | precision |
---|---|---|---|
f | - | 4 | 2 |
Person doc = new Person("Doc", 65);
String.format("%30s", doc);
String.format("%30s", doc.toString()); //Die Ausgabe ist dieselbe
Auf diese Weise wird, wenn Sie ein Objekt als Argument übergeben, die Methode "toString" dieser Klasse aufgerufen und das Ergebnis als Zeichenfolge behandelt.
Wenn die an das Argument übergebene Klasse jedoch die Schnittstelle "Formatierbar" implementiert, ist es möglich, die Ausgabemethode anstelle des Ergebnisses von "toString" zu steuern.
Eine Schnittstelle, in der die folgenden Methoden definiert sind.
Formatierbare Schnittstelle
void formatTo(Formatter formatter,
int flags,
int width,
int precision)
Wenn Sie eine Instanz einer Klasse, die diese Schnittstelle implementiert, an eine printf-Methodenfamilie übergeben, wird die Methode formatTo
aufgerufen, um den entsprechenden Teil zu formatieren.
class Person implements Formattable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "{name=" + name + ",age=" + age + "}";
}
@Override
public void formatTo(Formatter formatter, int flags, int width, int precision) {
//Die Formatierung erfolgt durch Bearbeiten des Formatierers innerhalb der formatTo-Methode.
//So verwenden Sie String#Gleich wie Format
formatter.format("%s, %d years old.", name, age);
}
}
Beispiel für die Verwendung einer Klasse, die formatTo implementiert
Person marty = new Person("Marty", 17);
Person doc = new Person("Doc", 65);
System.out.printf("%s%n", marty); // Marty, 17 years old.
System.out.printf("%s%n", doc); // Doc, 65 years old.
System.out.println(marty); // {name=Marty,age=17}
System.out.println(doc); // {name=Doc,age=65}
Auf diese Weise kann die Ausgabe für das Debuggen frei verarbeitet werden, indem die Ausgabe für den Benutzer der Methode "formatTo" überlassen wird, so dass sie aus Sicht des Entwicklers einfach zu verwenden ist.
Noch praktischer ist, dass die formatTo
-Methode Gebietsschemainformationen verarbeiten kann.
Beispielsweise ist eine mehrsprachige Unterstützung wie folgt möglich.
@Override
public void formatTo(Formatter formatter, int flags, int width, int precision) {
if (formatter.locale() == Locale.JAPAN) {
formatter.format("%s ist%Ich bin d Jahre alt.", name, age);
} else {
formatter.format("%s, %d years old.", name, age);
}
}
Standardmäßig ist dies das Gebietsschema des Systems. Sie können die Anzeige jedoch ändern, indem Sie das Gebietsschema als Argument für printf übergeben.
System.out.printf("%s%n", doc); //Doc ist 65 Jahre alt.
System.out.printf(Locale.ENGLISH, "%s%n", doc); // Doc, 65 years old.
Da die Anzeigebreite an das Argument "formatTo" übergeben wird, ist es möglich, abhängig von der Ausgabebreite automatisch zu wechseln.
Beispiel für das automatische Umschalten der Yuan-Notation
private enum Gengou implements Formattable {
MEIJI("Meiji", 'M'), TAISHO("Taisho", 'T'), SHOWA("Showa", 'S'), HEISEI("Heisei", 'H');
private String name;
private char initialLetter;
private Gengou(String japaneseName, char initial) {
this.name = japaneseName;
this.initialLetter = initial;
}
/**
*Wenn die Anzeigebreite 2 oder mehr Zeichen beträgt, wird der offizielle Name ausgegeben, und wenn es 1 Zeichen ist, wird 1 alphabetisches Zeichen ausgegeben.
*/
@Override
public void formatTo(Formatter formatter, int flags, int width, int precision) {
if (width >= 2) {
formatter.format("%s", name);
} else {
formatter.format("%s", initialLetter);
}
}
}
System.out.printf("Dieses Jahr%3s 30 Jahre.%n", Gengou.HEISEI); // Dieses Jahr平成 30 年です。
System.out.printf("Dieses Jahr%1s 30 Jahre.%n", Gengou.HEISEI); // Dieses JahrH 30 年です。
Da das Argument von "formatTo" frei gehandhabt werden kann, ist es möglich, die Ausgabe der Klasse, die den Absatz (Absatz) darstellt, zu steuern, indem die Genauigkeit als Anzahl der Auslassungszeichen und die Breite als Gesamtzahl der Zeichen verwendet werden.
private static class Paragraph implements Formattable {
private String content;
public Paragraph(String content) {
this.content = content;
}
@Override
public void formatTo(Formatter formatter, int flags, int width, int precision) {
StringBuilder sb = new StringBuilder();
if (content.length() < width) {
sb.append(content);
} else {
sb.append(content.substring(0, width - precision));
if (precision > 0) {
for (int i = 0; i < precision; i++) {
sb.append(".");
}
}
}
formatter.format(sb.toString());
}
}
Paragraph p = new Paragraph("This is very very long text!");
System.out.printf("%15.3s%n", p); // This is very...
System.out.printf("%10.4s%n", p); // This i....
Als ich den Artikel schrieb, bemerkte ich, dass es meiner Meinung nach einfacher ist, eine separate Klasse für die Nachrichtenformatierung zu erstellen, wenn die Ausgabe Text wie die im Beispiel erwähnte Klasse "Person" ist.
Im Gegenteil, im Fall einer wertobjektähnlichen Klasse, die eine Bedeutung als Wert für sich hat, wie "Betrag" und "Benutzer-ID", halte ich es immer für gut, "Formatierbar" zu implementieren. ..
Recommended Posts