Google Groups unterstützt keine neuen Usenet-Beiträge oder ‑Abos mehr. Bisherige Inhalte sind weiterhin sichtbar.

Re: Kontrollierte Ausnahmen und Schnittstellen

0 Aufrufe
Direkt zur ersten ungelesenen Nachricht
Die Nachricht wurde gelöscht
Die Nachricht wurde gelöscht

Ralf Ullrich

ungelesen,
19.07.2007, 16:18:1819.07.07
an
Stefan Ram wrote:

> Habe ich noch eine Lösung dieses Dilemmas übersehen?

Siehe die mit XXX markierten Stellen.

cu

package de.jnana.dclj.demo.genex;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URI;

public class GenericExceptionDemo {

// First: decide how many different _checked_ exception you want
// to support. For this demo I choose two.

interface Value<V, E1 extends Throwable, E2 extends Throwable> {
V value() throws E1, E2;
}

// Now some conveniences for cases when less than the supported
// number of checked exceptions (CE) are needed.

interface ValueNoCE<V> extends Value1CE<V, RuntimeException> {
//
}

interface Value1CE<V, E1 extends Throwable> extends
Value<V, E1, RuntimeException> {
//
}

// Now a demonstration where such a generic Value instance is used:

static <V, E1 extends Throwable, E2 extends Throwable> void printAll(
PrintStream out, Value<V, E1, E2>... values) throws E1, E2 {
for (Value<V, E1, E2> v : values) {
out.println(v.value());
}
}

// Now two concrete Value classes

static class LazyAbsoluteFileValue implements ValueNoCE<File> {

final URI uri;

public LazyAbsoluteFileValue(URI uri) {
this.uri = uri;
}

@Override
public File value() {
return new File(uri).getAbsoluteFile();
}
}

static class LazyCanonicalFileValue implements Value1CE<File, IOException> {

final URI uri;

public LazyCanonicalFileValue(URI uri) {
this.uri = uri;
}

@Override
public File value() throws IOException {
return new File(uri).getCanonicalFile();
}
}

// And finally all put together:
public static void main(String[] args) {
URI[] uris = map(new URIFactory(), File.listRoots());

LazyAbsoluteFileValue[] lafs = map(new LAFVFactory(), uris);
printAll(System.out, lafs); // nothing to catch here XXX

LazyCanonicalFileValue[] lcfs = map(new LCFVFactory(), uris);
try {
printAll(System.out, lcfs); // need to catch IOE here XXX
} catch (IOException e) {
e.printStackTrace();
}
}

// some helpers used in this demo:
interface OneArgFactory<A, L> {
L create(A arg);
}

static class URIFactory implements OneArgFactory<File, URI> {
@Override
public URI create(File arg) {
return arg.toURI();
}
}

static class LAFVFactory implements
OneArgFactory<URI, LazyAbsoluteFileValue> {
@Override
public LazyAbsoluteFileValue create(URI arg) {
return new LazyAbsoluteFileValue(arg);
}
}

static class LCFVFactory implements
OneArgFactory<URI, LazyCanonicalFileValue> {
@Override
public LazyCanonicalFileValue create(URI arg) {
return new LazyCanonicalFileValue(arg);
}
}

@SuppressWarnings("unchecked")
static <A> A[] newArray(int length) {
return (A[]) new Object[length];
}

static <D, T> T[] map(OneArgFactory<D, T> factory, D... data) {
T[] rv = newArray(data.length);
for (int i = 0; i < data.length; i++) {
rv[i] = factory.create(data[i]);
}
return rv;
}

}

Olaf Willuhn

ungelesen,
20.07.2007, 07:06:3720.07.07
an
Stefan Ram wrote:

> Man möchte eine universelle Schnittstelle definieren:
>
> interface Value { java.lang.Object value(); }
>
> Eine Implementation
>
> Class ExampleValue
> implements Value
> { public java.lang.Object value()
> throws java.io.IOException
> { return MyFile.readValue(); }}
>
> scheitert jedoch daran, daß die Schnittstelle die
> kontrollierte Ausnahme »java.io.IOException« nicht enthält.
[...]


> Habe ich noch eine Lösung dieses Dilemmas übersehen?

Ja, man definiert weniger allgemeine Interfaces.

Wenn eine Schnittstelle so nichtssagend wie "Value"
ist, dass der Benutzer dieser API nicht mal annaehernd
erkennen kann, welchen Zweck sie eigentlich erfuellt,
dann kann man sie sich auch schenken und stattdessen
einfach java.lang.Object nehmen.

Aber das ist nur meine Meinung.

Gruss
Olaf

Die Nachricht wurde gelöscht

Ralf Ullrich

ungelesen,
20.07.2007, 11:54:5120.07.07
an
Stefan Ram wrote:

>"Ralf Ullrich" <ne...@jnana.de> writes:
>>static class LazyAbsoluteFileValue implements ValueNoCE<File>

>>static class LazyCanonicalFileValue implements Value1CE<File, IOException>
>

> Vielleicht habe ich noch einen Aspekt Deines Vorschlags nicht
> verstanden. Aber der Nutzen einer universellen Schnittstelle,

Ja, das denke ich auch.

> soll es sein, daß alle ihre Implementationen überall dort
> verwendet werden können, wo nur diese Schnittstelle
> vorausgesetzt wird.
>
> Für Java sind »ValueNoCE« und »Value1CE« zwei verschiedne
> Schnittstellen. Wenn ich jetzt universelle Algorithmen auf

ValueNoCE und Value1CE sind nur Convenience-Klassen für den Programmierer,
damit er sich ein wenig Tipparbeit (=die explizite und ggf. wiederholte
Angabe von RuntimeException) sparen kann. Der allgemeine Basis-Typ ist
Value.

> dieser Wert-Schnittstelle schreiben will, dann müßte ich also
> für jede der beiden Schnittstellen eine generische Version
> des Algorithmus' bereitstellen und warten.

Nein, du musst den Algorithmus nur für Value schreiben, siehe das Beispiel
printAll() aus meinem Vorschlag.

> Dabei wurde mehrfach Gebrauch von der folgenden Schnittstelle gemacht:
>
>public interface ParameterizedValue /* de.dclj.ram.ParameterizedValue */
><Domain,Range>
>{ Range of( Domain value ); }

Und diese würdest du in meinem Vorschlag nun so modifzieren, dass sie die
gewünschte maximale Zahl an verschiedenen Exceptions unterstützt. (Siehe
erste Kommentare aus meinem ursprünglichen Beispiel.) Nehmen wir wieder
mal an wir wollen bis zu zwei verschiedene Exceptions unterstützen:

package de.dclj.ram;
/**
* @param <D> Type of Domain
* @param <R> Type of Range
* @param <E1> Type of first possible Exception
* @param <E2> Type of second possible Exception
*/
public interface ParameterizedValue<D, R,


E1 extends Throwable, E2 extends Throwable>{

R of( D value ) throws E1, E2;
}

> Die Methode »of« durchsucht nun alle Abbildungen von oben nach
> unten, bis sie eine Definition des Namens »value« findet.

Kannst du auch weiterhin, es folgt gleich die angepasste StackOfMaps.
Zuvor noch ein Wort dazu:

> Ich hoffe, daß ich hier von Schnittstellen ohne weitere
> Bedeutung (wie »ParameterizedValue«) sinnvollen Gebrauch
> gemacht habe.

Jein. Deine Deklaration:

public class StackOfMaps<D,R>
implements Stack<ParameterizedValue<D,R>>, ParameterizedValue<D,R> {...}

Lässt keine typsichere Anwendung von z.B. push() oder pop() zu und
verfehlt damit den Zweck weshalb man Generics einsetzt. Du musst also noch
die konkrete Subklasse von ParameterizedValue als Typparameter hinzufügen:

public class StackOfMaps<D,R,V extends ParameterizedValue<D,R>>
implements Stack<V>, ParameterizedValue<D,R> {...}

Wobei sich hier durch dein Design immer noch ein wenig wünschenswerter
allerdings unlösbarer "Typkonflikt" ergibt, da die StackOfMaps
idealerweise zuweisung-kompatibel zu V sein sollte, aber ein solche
Deklaration nicht zulässig ist:

public class StackOfMaps<D,R,V extends ParameterizedValue<D,R>>
implements Stack<V>, V {...}

Bleiben wir mal bei dem was machbar ist:

package de.dclj.ram.system.stack;

import de.dclj.ram.system.stack.Stack;
import de.dclj.ram.system.stack.ArrayStack;
import de.dclj.ram.ParameterizedValue;

public class StackOfMaps<D,R,E1 extends Throwable,
E2 extends Throwable, V extends ParameterizedValue<D,R,E1,E2>>
implements Stack<v>, ParameterizedValue<D,R,E1,E2> {

ArrayStack<V> stack = new ArrayStack<V>();

public boolean isEmpty() {
return stack.isEmpty();
}

public void push( V map ) {
stack.push( map );
}

public V pop() {
return stack.pop();
}

public R of( D value ) throws E1,E2 {
for( int i = 0; i < stack.size(); ++i ) {
V map = stack.peek( i );
R result = map.of( value );
if( result != null ) {
return result;
}
}
return null;
}
}

> Gleichzeitig kann man erkennen, daß es aufwendig wäre,
> dasselbe nun noch einmal für eine Variante der Schnittstelle
> zu schreiben, die eine Ausnahme oder zwei Ausnahmen wirft -
> und diese Varianten dann auch immer noch zu pflegen.

Du musst nur eine Variante schreiben und pflegen, und zwar die mit der
höchsten Anzahl an Exceptions die du jemals unterstützen möchtest. Du
kannst diese unterstützten Exceptions später in der Anwendung durch
konkrete Klassen wieder "ausschalten" indem du sie als RuntimeExceptions
deklarierst, und bist dann folglich auch nicht gezwungen sie bei diesen
konkreten Anwendungen abzufangen:

// implements a map in memory, accessing it cannot result in exceptions
class InMemoryParametrizedValue<D,R>
implements ParameterizedValue<D,R,RuntimeException,RuntimeException> { ... }

StackOfMaps<String, Double, RuntimeException,RuntimeException, InMemoryParameterized<String, Double>> stack = ...;

System.out.println(stack.of("hello")); // kein try/catch nötig weil InMemory...

cu

Die Nachricht wurde gelöscht
0 neue Nachrichten