![translation](https://cdn.durumis.com/common/trans.png)
Dies ist ein von KI übersetzter Beitrag.
Sprache auswählen
Von durumis AI zusammengefasster Text
- Statische Fabrikmethoden sind eine Möglichkeit, Instanzen einer Klasse zu erstellen, anstatt Konstruktoren zu verwenden. Sie haben den Vorteil, dass sie Instanzen einen Namen geben können, die Leistung durch Caching von Objekten mit hohen Erstellungskosten verbessern und bei Bedarf andere Untertypen von Objekten zurückgeben können.
- Vor Java 8 war es nicht möglich, statische Methoden in Schnittstellen zu deklarieren, daher wurde eine Begleitklasse erstellt, um statische Fabrikmethoden zu definieren. Nach Java 8 können statische Methoden direkt zu Schnittstellen hinzugefügt werden, sodass es nicht mehr erforderlich ist, Begleitklassen separat zu definieren.
- Die Verwendung statischer Fabrikmethoden fördert die Komposition anstelle der Vererbung und kann sogar ein Vorteil sein, wenn es darum geht, unveränderliche Typen zu erstellen, da diese Einschränkung eingehalten werden muss. Da statische Fabrikmethoden jedoch nicht so deutlich in der API-Dokumentation wie Konstruktoren erscheinen, müssen Entwickler die API-Dokumentation gut schreiben und die Methoden mit allgemein bekannten Konventionen benennen, um Probleme zu minimieren.
Überblick
Die traditionelle Methode, um eine Instanz einer Klasse zu erhalten, ist der öffentliche Konstruktor.
public class Member {
private String name;
private int age;
private String hobby;
private MemberStatus memberStatus;
public Member(String name, int age, String hobby, MemberStatus memberStatus) {
this.name = name;
this.age = age;
this.hobby = hobby;
this.memberStatus = memberStatus;
}
}
public enum MemberStatus {
ADVANCED,
INTERMEDIATE,
BASIC;
Normalerweise reichen öffentliche Konstruktoren aus, aber wenn neben Konstruktoren auch statische Factory-Methoden (static factory method) bereitgestellt werden, ist es für Benutzer oft einfacher, Instanzen gemäß ihren Absichten zu erstellen.
Ein typisches Beispiel für statische Factory-Methoden ist die valueOf()-Methode von Boolean.
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
Diese Methode akzeptiert einen Wert vom Basistyp boolean und gibt ein Boolean-Objekt zurück.
Vorteile von statischen Factory-Methoden
Sie können einen Namen haben.
Die an den Konstruktor übergebenen Parameter und der Konstruktor selbst beschreiben die Eigenschaften des zurückgegebenen Objekts nicht ausreichend. Nehmen wir zum Beispiel die Hauptklasse des Member-Konstruktors (name, age, hobby, memberStatus). Es ist schwierig zu erkennen, welche Eigenschaften ein Mitglied hat, wenn man nur den Konstruktor betrachtet.
Außerdem kann man mit einer Signatur nur einen Konstruktor erstellen, während statische Factory-Methoden einen Namen haben und somit mit einer Signatur mehrere statische Factory-Methoden erstellen können, um Instanzen zurückzugeben.
public class Member {
private String name;
private int age;
private String hobby;
private MemberStatus memberStatus;
public Member(String name, int age, String hobby, MemberStatus memberStatus) {
this.name = name;
this.age = age;
this.hobby = hobby;
this.memberStatus = memberStatus;
}
public static Member basicMember(String name, int age, String hobby) {
return new Member(name, age, hobby, MemberStatus.BASIC);
}
public static Member intermediateMember(String name, int age, String hobby) {
return new Member(name, age, hobby, MemberStatus.INTERMEDIATE);
}
public static Member advancedMember(String name, int age, String hobby) {
return new Member(name, age, hobby, MemberStatus.ADVANCED);
}
Wenn man mehrere statische Factory-Methoden mit der gleichen Signatur erstellt, anstatt den MemberStatus über den Konstruktor zu unterscheiden, kann der Benutzer eine Instanz eines Mitglieds mit bestimmten Fähigkeiten erstellen, ohne Verwirrung zu stiften.
Betrachten wir die in JDK definierte Bibliothek, so gibt es die statische Factory-Methode probablePrime() von BigInteger.
public static BigInteger probablePrime(int bitLength, Random rnd) {
if (bitLength < 2)
throw new ArithmeticException("bitLength < 2");
return (bitLength < SMALL_PRIME_THRESHOLD ?
smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));
Wenn man den Standardkonstruktor von BigInteger mit der statischen Factory-Methode probablePrime() vergleicht, ist es klar, dass Letztere den Satz „Gibt ein BigInteger-Objekt zurück, dessen Wert eine Primzahl ist“ besser beschreibt.
Sie müssen bei jedem Aufruf keine neue Instanz erstellen.
public static Boolean valueOf(boolean b) {
return (b ? Boolean.TRUE : Boolean.FALSE);
Man kann sehen, dass die valueOf()-Methode von Boolean eine Instanz zwischenspeichert und dann zurückgibt. Dieses Merkmal kann die Leistung erheblich verbessern, wenn Objekte mit hohen Erstellungskosten häufig angefordert werden.Flyweight-Musterkann als eine ähnliche Technik betrachtet werden.
Klassen, die statische Factory-Methoden verwenden, um bei wiederholten Anfragen dasselbe Objekt zurückzugeben, werden als Instanzsteuerungsklassen bezeichnet, da sie die Lebensdauer von Instanzen steuern können. Die Instanzsteuerung ermöglicht es, Singleton-Klassen zu erstellen oder Klassen zu erstellen, die nicht instanziiert werden können. Außerdem kann es in unveränderlichen Klassen sichergestellt werden, dass es nur eine einzige Instanz gibt.
Die Instanzsteuerung ist die Grundlage des Flyweight-Musters, und Aufzählungsdatentypen gewährleisten, dass nur eine einzige Instanz erstellt wird.
Beispiel
In Minecraft müssen Bäume gepflanzt werden. Wenn für jedes Baumobjekt ein neues Objekt erstellt wird, besteht die Gefahr eines Memory-Overflows.
Daher kann man die roten und grünen Baumobjekte speichern und nur ihre Positionen ändern, bevor man sie zurückgibt. Natürlich kann es mehr als zwei Farben geben, daher wäre es effizient, die Bäume nach Farbe in einer Datenstruktur wie einer Map zu speichern.
public class Tree {
// Ein Baum hat die folgenden drei Informationen.
private String color;
private int x;
private int y;
// Konstruktor, der nur durch die Farbe erstellt wird.
public Tree(String color) {
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
// Beim Pflanzen eines Baumes
public void install(){
System.out.println("x:"+x+" y:"+y+" An dieser Stelle wurde ein "+color+"-farbiger Baum installiert!");
}
}
public class TreeFactory {
// HashMap-Datenstruktur zum Verwalten von erstellten Bäumen.
public static final Map treeMap = new HashMap<>();
public static Tree getTree(String treeColor){
// Suchen Sie in der Map nach einem Baum mit der eingegebenen Farbe. Wenn er vorhanden ist, geben Sie dieses Objekt zurück.
Tree tree = (Tree)treeMap.get(treeColor);
// Wenn in der Map noch kein Baum mit der gleichen Farbe vorhanden ist, erstellen Sie ein neues Objekt und geben Sie es zurück.
if(tree == null){
tree = new Tree(treeColor);
treeMap.put(treeColor, tree);
System.out.println("Neues Objekt erstellt");
}
return tree;
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Geben Sie die gewünschte Farbe ein :)");
for(int i=0;i<10;i++){
// Baumfarbe eingeben
String input = scanner.nextLine();
// Holen Sie sich einen Baum aus der Fabrik.
Tree tree = (Tree)TreeFactory.getTree(input);
// Setzen Sie die Position x, y des Baumes.
tree.setX((int) (Math.random()*100));
tree.setY((int) (Math.random()*100));
// Baum installieren.
tree.install();
}
}
Unterschied zum Singleton-Muster
Das Singleton-Muster erlaubt es, nur einen einzigen Baum in der Baumklasse zu erstellen. Daher müsste man die Farbe des erstellten einzelnen Objekts ändern, wenn man das Singleton-Muster verwendet. Das bedeutet, dass man mit dem Singleton-Muster nur einen einzigen Baum haben kann, unabhängig von der Art.
Einsatzmöglichkeiten
Das Flyweight-Muster wird im String Constant Pool von Java verwendet.
Sie können ein Objekt eines Untertyps des Rückgabetyps zurückgeben.
Wenn Sie die asList()-Methode der Arrays-Dienstprogramm-Klasse verwendet haben, können Sie diesen Vorteil verstehen.
public static List asList(T... a) [
return new ArrayList<>(a);
Es wird ein Wert in eine ArrayList, eine Unterimplementierung von List, verpackt und zurückgegeben, wobei der Benutzer diese Implementierung nicht kennen muss. Mit anderen Worten, die Flexibilität, die Klasse des zurückgegebenen Objekts frei wählen zu können, ermöglicht es dem Entwickler, die Implementierung zu verbergen und die Implementierung zurückzugeben, wodurch die API klein gehalten werden kann.
Über statische Methoden in Java-Schnittstellen
Vor Java 8 konnten in Schnittstellen keine statischen Methoden deklariert werden. Wenn also eine statische Methode erforderlich war, die eine Schnittstelle mit dem Namen „Type“ zurückgab, wurde eine Begleitklasse namens „Types“ erstellt, die nicht instanziiert werden konnte und die Methode enthielt.
Ein bekanntes Beispiel sind die 45 Dienstprogramm-Implementierungen, die von JCF bereitgestellt werden, von denen die meisten über statische Factory-Methoden in einer einzigen Begleitklasse namens java.util.Collections erhalten werden. Insbesondere gibt es unter diesen Implementierungen einige, die nicht öffentlich sind und daher nur über statische Factory-Methoden instanziiert werden können (diese Implementierungen können natürlich nicht vererbt werden).
Außerdem werden die 45 Implementierungen nicht offengelegt, was zu einer viel kleineren API führt.
// Beispiel für eine Schnittstelle und eine Begleitklasse
Ab Java 8 können jedoch statische Methoden direkt zu Schnittstellen hinzugefügt werden, sodass keine separate Begleitklasse mehr definiert werden muss.
Es ist möglich, je nach Eingabeparameter ein Objekt einer anderen Klasse zurückzugeben.
Neben der einfachen Rückgabe eines Untertyps kann man je nach Wert des Parameters einen anderen Untertyp zurückgeben. Wenn man beispielsweise den MemberStatus je nach Punktzahl zurückgeben möchte, kann man eine statische Factory-Methode wie folgt erstellen und dort die Vergleichslogik einfügen.
public enum MemberStatus {
ADVANCED(80, 100),
INTERMEDIATE(50, 79),
BASIC(0, 49);
private final int minScore;
private final int maxScore;
MemberStatus(int minScore, int maxScore) {
this.minScore = minScore;
this.maxScore = maxScore;
}
public static MemberStatus of(int score) {
return Arrays.stream(values())
.filter(decideMemberStatus(score))
.findAny()
.orElseThrow(() -> new NoSuchElementException("Kein MemberStatus-Objekt gefunden."));
}
private static Predicate decideMemberStatus(int score) {
return element -> element.minScore <= score && element.maxScore >= score;
}
}
@DisplayName("MemberStatus-Test")
class MemberStatusTest {
@ParameterizedTest
@CsvSource(value = {"0:BASIC", "30:BASIC", "50:INTERMEDIATE", "70:INTERMEDIATE", "80:ADVANCED", "100:ADVANCED"}, delimiter = ':')
@DisplayName("Gibt MemberStatus je nach Punktzahl unterschiedlich zurück.")
void of(int input, MemberStatus expected) {
assertThat(MemberStatus.of(input)).isEqualTo(expected);
}
Zum Zeitpunkt des Schreibens der statischen Factory-Methode muss die Klasse des zurückzugebenden Objekts nicht existieren.
Im obigen Satz bezieht sichKlasse des Objektsauf unsere erstellte Klassendatei.
Als Hinweis: Class> bezieht sich auf das Class-Objekt, das vom Classloader beim Laden einer Klasse auf dem Heap zugewiesen wird. Dieses Class-Objekt enthält verschiedene Metadaten unserer erstellten Klasse.
package algorithm.dataStructure;
public abstract class StaticFactoryMethodType {
public abstract void getName();
public static StaticFactoryMethodType getNewInstance() {
StaticFactoryMethodType temp = null;
try {
Class> childClass = Class.forName("algorithm.dataStructure.StaticFactoryMethodTypeChild"); // Reflexion
temp = (StaticFactoryMethodType) childClass.newInstance(); // Reflexion
} catch (ClassNotFoundException e) {
System.out.println("Klasse nicht gefunden.");
} catch (InstantiationException e) {
System.out.println("Kann nicht im Speicher platziert werden.");
} catch (IllegalAccessException e) {
System.out.println("Fehler beim Zugriff auf die Klassendatei.");
}
return temp;
}
Im obigen Code kann man sehen, dass ein Class-Objekt über die Position der Implementierung der Schnittstelle erstellt wird und die tatsächliche Implementierung mithilfe der Reflexionstechnik initialisiert wird. Zu diesem Zeitpunktzum Zeitpunkt des Schreibens der statischen Factory-Methodemuss die Klasse StaticFactoryMethodTypeChild nicht existieren.
Wenn zum Zeitpunkt der Verwendung der statischen Factory-Methode keine Implementierung im Pfad algorithm.dataStructure.StaticFactoryMethodTypeChild vorhanden ist, tritt ein Fehler auf. Aber zum Zeitpunkt des Schreibens der statischen Factory-Methode gibt es kein Problem, daher ist es flexibel.
public interface Test {
int sum(int a, int b);
// Test ist eine Schnittstelle und es gibt keine Implementierung, daher tritt zum Zeitpunkt des Schreibens der statischen Factory-Methode kein Problem auf.
static Test create() {
return null;
}
}
public class Main {
public static void main(String[] args) {
Test test = Test.create();
System.out.println(test.sum(1, 2)); // NPE tritt auf
}
Man kann dieselbe Flexibilität erreichen, ohne Reflexion zu verwenden. Die statische Factory-Methode create() von Test zeigt, dass zum Zeitpunkt des Schreibens kein Problem auftritt, auch wenn keine Implementierung vorhanden ist. Natürlich tritt zum Zeitpunkt der tatsächlichen Verwendung eine NPE auf, daher muss später eine Implementierung zurückgegeben werden.
Diese Flexibilität ist die Grundlage für die Erstellung von Service Provider Frameworks, von denen JDBC ein typisches Beispiel ist. Der Anbieter eines Service Provider Frameworks ist die Implementierung des Service, und das Framework steuert die Bereitstellung dieser Implementierungen an den Client, um den Client von der Implementierung zu trennen (DIP).
- Komponenten eines Service Provider Frameworks
- Service-Schnittstelle
- Definiert das Verhalten der Implementierung.
- JDBC-Verbindung
- API zur Anbieterregistrierung
- Der Anbieter registriert die Implementierung.
- JDBC-DriverManager.registerDriver()
- API zum Zugriff auf Dienste
- Wird verwendet, wenn der Client eine Instanz des Service abruft. Wenn keine Bedingung angegeben wird, wird die Standardimplementierung oder die unterstützten Implementierungen abwechselnd zurückgegeben.
- Relevanz von statischen Factory-Methoden
- JDBC-DriverManager.getConnection()
- (optional) Service Provider Interface
- Wenn dies nicht vorhanden ist, muss die Reflexion verwendet werden, um jede Implementierung zu instanziieren.
- JDBC-Treiber
- Service-Schnittstelle
Das Service Provider Framework-Muster hat viele Variationen, darunter das Bridge-Muster, Dependency Injection Frameworks usw.
Typisches JDBC-Beispiel
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection connection = null;
connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ORA92", "root", "root");
Normalerweise wird JDBC wie oben geschrieben. Class.forName() wird verwendet, um OracleDriver zu registrieren, eine der Implementierungen von Driver. DriverManager.getConnection() wird verwendet, um eine Verbindung zur Implementierung von OracleDriver zu erhalten, eine der Implementierungen von Connection.
Dabei ist Connection die Service-Schnittstelle, DriverManager.getConnection() ist die Service-Zugriffs-API und Driver ist die Service Provider-Schnittstelle. Die API zur Anbieterregistrierung, DriverManager.registerDriver(), wurde jedoch nicht verwendet. Trotzdem können wir OracleDriver, eine Implementierung von Driver, mit Class.forName() allein registrieren. Wie ist das möglich?
Funktionsweise von Class.forName()
Wenn man diese Methode mit dem Namen der physischen Klassendatei als Argument aufruft, wird die JVM aufgefordert, diese Klasse zu laden. Der Classloader speichert die Metadaten der Klasse im Methodenbereich und weist gleichzeitig ein Class-Objekt auf dem Heap zu. Außerdem wird nach Abschluss des Klassengeladensder statische Feld- und Blockinitialisierungsvorgang durchgeführt, und in diesem Schritt wird die API zur Anbieterregistrierung verwendet.
public class OracleDriver implements Driver {
static {
defaultDriver = null;
Timestamp timestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");
try {
if (defaultDriver == null) {
defaultDriver = new OracleDriver();
DriverManager.registerDriver(defaultDriver); // OracleDriver registrieren
}
} catch (RuntimeException runtimeexception) {
} catch (SQLException sqlexception) {
}
}
...
Tatsächlich kann man sehen, dass in OracleDriver im statischen Block DriverManager.registerDriver() verwendet wird, um OracleDriver, die Implementierung von Driver, zu registrieren.
Analyse der DriverManager-Klasse
public class DriverManager {
private DriverManager() {
}
private static final Map drivers = new ConcurrentHashMap();
public static final String DEFAULT_DRIVER_NAME = "default";
public static void registerDefaultPrivider(Driver d) {
System.out.println("Driver registrieren");
registerDriver(DEFAULT_DRIVER_NAME, d);
}
public static void registerDriver(String name, Driver d) {
drivers.put(name, d);
}
public static Connection getConnection() {
return getConnection(DEFAULT_DRIVER_NAME);
}
public static Connection getConnection(String name) {
Driver d = drivers.get(name);
if (d == null) throw new IllegalArgumentException();
return d.getConnection();
}
Die DriverManager-Klasse ist in Wirklichkeit viel komplexer, aber wenn man sich auf das Wesentliche konzentriert, ist sie der oben beschriebenen ähnlich. Wie bereits erwähnt, wird registerDriver() in der statischen Blockanweisung von OracleDriver aufgerufen, um OracleDriver zu registrieren, und der Benutzer kann eine Implementierung von Connection abrufen, indem er getConnection() aufruft.
Wenn man sich die Service-Zugriffs-API getConnetion() genauer ansieht, stellt man fest, dass eine Verbindung aus der Driver-Schnittstelle abgerufen wird. Wenn es die Service Provider-Schnittstelle Driver nicht gäbe, müsste man Reflexion wie Class.forName() verwenden, um die gewünschte Connection-Implementierung zurückzugeben. Zu diesem Zeitpunktmuss die Connection-Implementierung zum Zeitpunkt der Erstellung der statischen Factory-Methode nicht existieren.
Stattdessen verwenden wir die Driver-Schnittstelle, registrieren dynamisch die Implementierung von Driver und können dann einfach eine Implementierung von Connection abrufen, die zu diesem Driver passt.
Als Hinweis: Ich habe den tatsächlichen JDK-Code der getConnection()-Methode von DriverManager analysiert. Wenn Sie nicht daran interessiert sind, können Sie dies überspringen.
@CallerSensitive
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
Zuerst wird die öffentliche statische Methode getConnection() aufgerufen, wobei url, Properties und CallerClass als Argumente an die private statische Methode getConnection() übergeben werden. Reflection.getCallerClass() dient dazu, die Klasse abzurufen, die die öffentliche statische Methode getConnection() aufgerufen hat. Wenn die Klasse Car getConnection() aufgerufen hat, kann Reflection.getCallerClass() das Class-Objekt abrufen.
private static Connection getConnection(String url, java.util.Properties info, Class> caller) throws SQLException {
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("Die URL darf nicht null sein", "08001");
}
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
}
}
if (reason != null) {
throw reason;
}
throw new SQLException("Kein geeigneter Treiber für "+ url gefunden", "08001");
callerCL ist ein Classloader-Objekt, das vom Classloader von caller oder dem aktuellen Thread erstellt wird. Anschließend wird aDriver nacheinander aus registeredDrivers, der Liste der in der aktuellen Anwendung registrierten Treiber, entnommen. Wenn dieser Driver von isDriverAllowed() als true erkannt wird, wird mithilfe dieses Drivers ein Connection-Objekt abgerufen und zurückgegeben. isDriverAllowed() prüft, ob aDriver in caller vorhanden ist.
Vorteile des JDBC-Frameworks
Der springende Punkt des JDBC-Frameworks ist, dass die Schnittstellen Driver und Connection sowie die Klassen, die diese Schnittstellen implementieren, vollständig getrennt bereitgestellt werden. Indem man die Schnittstellen verwendet, um einen Rahmen zu erstellen, und dann separate Implementierungsklassen zu diesem Rahmen erstellt, erhält man einen sehr flexiblen Ansatz.
So kann jeder DBMS-Anbieter die Schnittstellen Driver und Connection implementieren und diese bereitstellen, so dass Java-Entwickler dieselbe API für verschiedene DBMS-Treiber verwenden können.
Nachteile von statischen Factory-Methoden
Wenn man erbt, benötigt man einen öffentlichen oder geschützten Konstruktor. Wenn man nur statische Factory-Methoden bereitstellt, kann man keine Unterklassen erstellen.
Diese Einschränkung ist jedoch eher ein Vorteil, da sie eher zu Komposition als zu Vererbung führt und diese Einschränkung eingehalten werden muss, um unveränderliche Typen zu erstellen.
Statische Factory-Methoden sind für Programmierer schwer zu finden.
Da sie nicht so deutlich in der API-Beschreibung wie Konstruktoren dargestellt werden, müssen Entwickler die API-Dokumentation gut schreiben und die Methoden benennen, indem sie allgemein bekannte Konventionen befolgen, um das Problem zu entschärfen.
Benennung von statischen Factory-Methoden
- from
- Nimmt ein Argument entgegen und gibt eine Instanz dieses Typs zurück.
- Date date = Date.from(instant);
- of
- Nimmt mehrere Argumente entgegen und gibt eine Instanz des entsprechenden Typs zurück.
- Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
- valueOf
- Detailliertere Version von from und of.
- BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
- instance oder getInstance
- Gibt die Instanz zurück, die durch den Parameter angegeben wird, garantiert aber nicht, dass es sich um dieselbe Instanz handelt.
- StackWalker luke = StackWalker.getInstance(options);
- create oder newInstance
- Wie instance oder getInstance, garantiert aber, dass jedes Mal eine neue Instanz erstellt und zurückgegeben wird.
- Object newArray = Array.newInstance(classObject, arraylen);
- getType
- Wie getInstance, wird aber verwendet, wenn die Factory-Methode nicht in der zu erstellenden Klasse, sondern in einer anderen Klasse definiert ist.
- FileStore fs = Files.getFileStore(path);
- newType
- Wie newInstance, wird aber verwendet, wenn die Factory-Methode nicht in der zu erstellenden Klasse, sondern in einer anderen Klasse definiert ist.
- BufferedReader br = Files.newBufferedReader(path);
- type
- Kompakte Version von getType und newType.
- List<Complaint> litany = Collections.list(legacyLitany);
Zusammenfassung
Statische Factory-Methoden und öffentliche Konstruktoren haben jeweils ihren eigenen Zweck, daher sollten Sie sie entsprechend verwenden.