[JAVA] Schreiben Sie beim Definieren einer Klasse sowohl formatTo als auch toString (Verwendung von Formattable).

In 3 Zeilen

Einführung

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.

toString-Methode

Zunächst die bekannte toString-Methode.

Überblick

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

Beispiel

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.

Vorteile der Verwendung von toString

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.

Probleme mit toString

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

Zeichenfolge formatieren

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.

//
//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

Verarbeitung, wenn ein Objekt als Argument übergeben wird

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.

Formatierbare Schnittstelle

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.

Grundlegende Verwendung

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.

Mehrsprachig

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.

Anwendungsbeispiel ... 1

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 年です。

Anwendungsbeispiel ... 2

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....

Wo zu verwenden

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

Schreiben Sie beim Definieren einer Klasse sowohl formatTo als auch toString (Verwendung von Formattable).
[Rails] Wie schreibe ich, wenn ich eine Unterabfrage mache?
Verwendung der Java-Klasse
Verwendung der Wrapper-Klasse
Verwendung von Klassenmethoden [Java]
[Java] Verwendung der Math-Klasse
Ein Memorandum zur Verwendung von Eclipse
[Java] Verwendung der File-Klasse
[Basic] So schreiben Sie ein Dockerfile Selbstlernend ②
[Einführung in Java] So schreiben Sie ein Java-Programm
Anzeigen von Fehlermeldungen und Erfolgsmeldungen bei der Registrierung als Benutzer
[Verarbeitung × Java] Verwendung der Klasse
Verwendung der Java Scanner-Klasse (Hinweis)
[Java] Verwendung der Calendar-Klasse
[SpringBoot] So schreiben Sie einen Controller-Test
Schienen: Wie man eine Rechenaufgabe schön schreibt
So konvertieren Sie einen Soliditätsvertrag in eine Java-Vertragsklasse
[Java] Verwendung der Kalenderklasse und der Datumsklasse
Ich möchte Combine auch in UIKit verwenden.
So führen Sie die SpringBoot-App als Dienst aus
So verwenden Sie ein Array für den TreeMap-Schlüssel
So schreiben Sie einen Komponententest für Spring Boot 2
java: Wie schreibe ich eine generische Typliste? [Hinweis]
So schreiben Sie eine Datumsvergleichssuche in Rails
Wie schreibe ich einen Core Mod in Minecraft Forge 1.15.2
Hinweise zur Überprüfung bei der Verwendung von Lombok
Verwendung und Anwendung der JFrame / Canvas-Klasse von Java
Verwendung von Map
Wie schreibe ich Rails
Wie benutzt man rbenv?
Verwendung mit_option
Verwendung von fields_for
Verwendung der Karte
Verwendung von collection_select
Wie benutzt man Twitter4J
Wie benutzt man active_hash! !!
Verwendung von MapStruct
Verwendung von TreeSet
[Verwendung des Etiketts]
Wie schreibe ich Docker-Compose
Wie man Identität benutzt
Wie man Hash benutzt
Wie schreibe ich Mockito
So schreiben Sie eine Migrationsdatei
Verwendung von Dozer.mapper
Wie benutzt man Gradle?
Verwendung von org.immutables
Verwendung von java.util.stream.Collector
Verwendung von VisualVM
Verwendung von Map
So fordern Sie mit jMeter eine CSV-Datei als JSON an
[1st] RSpec-Anfänger haben versucht, Model Spec als Anfänger zu schreiben
Verwendung von ArgumentMatchern wie Mockitos any () in Kotlin