2016-04-07 2 views
-2

meist JDK Methoden einen Charset (oder ein charset Namen) zu akzeptieren rekonfigurieren Charset(De|En)coder fehlerhafte Eingabe und nicht zuzuordnenden Zeichen zu handhaben mit CodingErrorAction.REPLACE:Warum verwenden die Methoden von Files CodingErrorAction.REPORT, um Codierungsfehler zu behandeln, während das gewöhnliche JDK-Verhalten REPLACE verwendet?

  • String ctors
  • ByteArrayOutputStream.toString()
  • InputStreamReader ctors
  • OutputStreamWriter ctors
  • PrintStream ctors
  • PrintWriter ctors
  • Formatter ctors
  • Scanner ctors
  • usw.

Auch wenn nie in der Javadoc ausgeführt, ist es einfach, dass in OpenJDK Quellcode oder mit einfachen Testfällen zu überprüfen:

private static final byte[] INVALID_UTF_8 = new byte[] {-1, 97}; 

@Test 
public void string_uses_replacement_characters() { 
    String str = new String(INVALID_UTF_8, StandardCharsets.UTF_8); 
    assertThat(str).isEqualTo("\uFFFDa"); 
} 

@Test 
public void inputStreamReader_uses_replacement_characters() throws IOException { 
    ByteArrayInputStream bais = new ByteArrayInputStream(INVALID_UTF_8); 
    InputStreamReader isr = new InputStreamReader(bais, StandardCharsets.UTF_8); 
    BufferedReader br = new BufferedReader(isr); 

    assertThat(br.readLine()).isEqualTo("\uFFFDa"); 
} 

Einige dieser Klassen definieren auch Methoden, die Charset(En|De)coder für diese akzeptieren wer möchte einen anderen CodingErrorAction angeben.

JDK 8 hinzugefügt die Files Klasse, die Dienstprogramm/Factory-Methoden bietet, um die Boilerplate durch einige sehr häufige Aktionen erforderlich zu reduzieren. Alle diese Methoden folgen jedoch nicht dem zuvor beschriebenen üblichen Verhalten. (En | De) Codierer werden nicht neu konfiguriert, um CodingErrorAction.REPLACE zu verwenden, und Ausnahmen werden auf ungültige Byte & nicht mappable Zeichen ausgelöst.

Kennt jemand die Vernunft dieser Änderung und warum niemand nützlich fand, es im Javadoc klar zu erklären?

Auch wenn ich denke, dass REPORT ein serielles Standardverhalten ist, scheint es wirklich fehleranfällig, stillschweigend diese stillschweigende Vereinbarung zu ändern, die vor Jahren gemacht wurde. Die meisten Entwickler würden erwarten, dass newBuffereReader(p, "UTF-8") zu new BufferedReader(new InputStreamReader(new FileInputStream(p), "UTF-8")) entspricht, was nicht wahr ist.

Hinweis: https://bugs.openjdk.java.net/browse/JDK-8143997 scheint im Zusammenhang mit meiner Frage.

+0

Dies wurde vorher nicht dokumentiert, und es ist immer noch nicht explizit dokumentiert. Ich bin mir also nicht sicher, wie wir antworten könnten, wie sich etwas, das nicht dokumentiert ist, geändert hat. Sie hätten sich nicht darauf verlassen sollen. – Tunaki

+1

Nur der vorhandene Code muss mit älteren Versionen kompatibel bleiben. Neuer Code kann sich anders verhalten, deshalb wurde der neue Code an erster Stelle hinzugefügt. – Holger

+0

@Tunaki: Ich weiß, dass es nicht dokumentiert wurde. Dennoch haben sich die Java-Entwickler in den letzten 10+ Jahren an das "übliche Verhalten" gewöhnt: Methoden, die behandeln, warum das Encoding codierungsbedingte Ausnahmen nicht nutzlos macht, fragen Sie. Jeder, der eine der aufgelisteten Klassen verwendet, verlässt sich auf dieses undokumentierte Verhalten. Ich finde es wirklich überraschend, dass diese Änderung in der Dokumentation nicht betont wird (sowohl in der neuen API, die den "üblichen Vertrag" bricht, als auch in der alten API, die als fehleranfällig zu gelten scheint, da REPLACE-default durch REPORT abgelöst wurde) -by-default) –

Antwort

1

Es scheint wirklich fehleranfällig, stillschweigend diese stillschweigende Vereinbarung zu ändern, die vor Jahren gemacht wurde. Die meisten Entwickler erwarten

Es gibt keine stillschweigende Vereinbarung. Gäbe es dann würden alle Details der Implementierung implizit Teil der Spezifikation sein und dies geschehen würde:

xkcd workflow
(source xkcd.com)

also bitte nicht auf Leer Heizung verlassen (wenn nicht anders angegeben).

Ein prominentes Beispiel, bei dem in der Vergangenheit das Verhalten, das nicht durch die Spezifikation garantiert wurde, geändert wurde, waren die Methoden Arrays.sort und Collections.sort.In der Vergangenheit tolerierten sie Komparatoren oder gleichwertige Implementierungen, die die durch die Spezifikation vorgeschriebene Transitivitätsanforderung verletzten. Wenn die Zusammenführungssortierung in TimSort geändert wurde, wurde eine Ausnahme hinzugefügt, die Verstöße gegen die Anforderung gemeldet hat. Dies war rückwärtskompatibel, aber innerhalb der Spezifikation, da solche Komparatoren nicht innerhalb der Spezifikation existieren können.

Also im Prinzip könnten die Entwickler sogar die alten Implementierungen geändert haben. Aber aus Gründen der Abwärtskompatibilität und weil es keine zwingende Notwendigkeit gab, dies zu tun, wählten sie nur das Verhalten zu besseren, vernünftigeren Praktiken bei neuen APIs.

Neue APIs sind eine Weiterentwicklung gegenüber alten APIs. Streams sind keine Collections sind keine Enumerations. ByteChannels sind keine IOSTreams.

+0

Technisch stimme ich dir zu, es ist völlig in Ordnung. Aus der Sicht der Stewardship wäre es sehr schön, solche Veränderungen zu dokumentieren. Wenn man sich dazu entschließt, ein Standardverhalten zu ändern, während man eine neue API einführt, ist wahrscheinlich etwas falsch mit dem alten (und es gab). Wenn es falsch und wichtig genug war, es zu ändern und einige "Inkonsistenzen" im gesamten JDK einzuführen, warum sollte nicht dokumentiert werden, dass alle alten APIs ein gefährliches Verhalten verwenden und das neue eher Korrektheit als Resilienz vorzieht? –

+0

Sie sprechen über Änderungen. Aber die alten APIs werden nicht geändert. Die neuen APIs sind neu und somit auch nicht verändert. Es gibt also keine Änderung am Dokument. – the8472