Typsichere Liste

Moin,

ich habe die Klassen ```import java.util.NoSuchElementException;

/**

  • A simple linked list. One may go through this list by {@link #advance()} until
  • the last position ({@link #endpos()}) is reached. One also may
  • {@link #delete()} and {@link #insert(Object)} elements. After advancing it is
  • possible to go back to the beginning by {@link #reset()}.

*/
public class List {

/**
* Reference on the first Entry of this List
/
private Entry begin;
/
*
* References before the actual Entry of this List
*/
private Entry pos;

/**
* Create a new empty List.
*/
public List() {
pos = begin = new Entry();
}

/**
* Determines if this List is empty or not.
*
* @return true, if there are no elements in this List
*/
public boolean empty() {
return begin.next == null;
}

/**
* Determines if it is possible to {@link #advance()} in this List. Returns
* true if the last position of this List has been reached. An
* {@link #empty()} List will alway deliver true
*
* @return true if the last Entry in this List already has been
* reached.
*/
public boolean endpos() { // true, wenn am Ende
return pos.next == null;
}

/**
* Returns to the beginning of this List.
*/
public void reset() {
pos = begin;
}

/**
* Advances one step in this List.
*
* @throws NoSuchElementException
* if the last Entry of this List already has been reached.
*/
public void advance() {
if (endpos()) {
throw new NoSuchElementException(“Already at the end of this List”);
}
pos = pos.next;
}

/**
* Returns the actual element of this List.
*
* @return the actual element
*
* @throws NoSuchElementException
* if the last Entry of this List already has been reached.
*/
public Object elem() {
if (endpos()) {
throw new NoSuchElementException(“Already at the end of this List”);
}
return pos.next.o;
}

/**
* Inserts o in this List. It will be placed before the actual
* element. After insertion the inserted element will become the actual
* element.
*
* @param x
* the element to be inserted
*/
public void add(Object x) {
Entry newone = new Entry(x, pos.next);

  pos.next = newone;

}

/**
* Deletes the actual element of this List. The element after the actual
* element will become the new actual element.
*
* @throws NoSuchElementException
* if the last Entry of this List already has been reached.
*/
public void delete() {
if (endpos()) {
throw new NoSuchElementException(“Already at the end of this List”);
}
pos.next = pos.next.next;
}
}```

und

/**
 * An Entry holds an Object <code>o</code> and a reference <code>next</code> to
 * the next Entry such that a linked List of Entry elements is generated.
 * 
 * 
 */
class Entry {

   Object o;
   Entry next;

   Entry() {
      this(null, null);
   }

   Entry(Object o) {
      this(o, null);
   }

   Entry(Object o, Entry e) {
      this.o = o;
      this.next = e;
   }

}
```  und ich soll jetzt eine typsichere Liste implementieren.

Meine erste Frage lautet ist es sinnvoller von der bestehenden Liste zu erben oder eine komplett neue Liste zu schreiben??

Meine erste Intuition ist "erben" da man sich warscheinlich Schreibarbeit spart?!?


mit freundlichen Grüßen:)

Ich verstehe die Aufgabe nicht so ganz. Sollst du den bestehenden Quellcode umschreiben? Ein neues Interface definieren? An welche Vorgaben musst du dich halten?
Wenn du typsicherheit umsetzen sollst, dann ist es am einfachsten, Generics zu verwenden.

es steht nicht direkt in der Aufgabe drin, aber ich denke ich soll Generics verwenden, weil das Thema in der Vorlesung war…

hier die komplette Aufgabenstellung :

http://www-lehre.inf.uos.de/~binf/2014/uebung/blatt6/blatt6.pdf (die zweite Aufgabe)

wobei mir das ein bischen seltsam vorkommt wenn eine generische Liste von einer “normalen” Object Liste erben würde

In dem pdf steht nirgends was mit Listen. Evtl. das falsche gepostet? Davon ab, würde ich -wenn ich Tutor wäre- von dir erwarten, dass Du den Code als Vorlgage nimmst und ihn so umbaust, dass statt Object generische Parameter/Rückgabewerte verwendet werden. Das hätte den gewünschten Lerneffekt beim Umgang mit Generics. Darüber hinaus ist Vererbung in diesem Fall technisch etwas schwierig. Du kannst nämlich Methoden nicht überschreiben und dabei ihren Parametertyp einschränken (was Du bei Generics ja gerade willst). In Deiner abgeleiteten Liste hättest Du zumnindest mit “add” noch eine typUNsichere Methode. Da müsstest Du also für eine entscheidende Funktionalität einer Liste eine neue Methode spendieren und die Verwendung des typunsicheren add ziemlich häßlich verhindern, indem Du dort eine Exception schmeißt oder dort auf den gewünschten Typen casten mit der Gefahr von ClassCastExceptions. Da aber der Sinn von Generics ist, schon zur Entwicklungszeit etwas mehr Typsicherheit zu haben und nicht erst zur Laufzeit ClassCastExceptions fliegen zu lassen, ist eine Vererbungslösung hier irgendwie absurd.

es steht schon im PDF, genau wie genannt zweite Aufgabe trotz Überschrift zu weiteren Sub-Thema

Aufgabe 6.2: Cloneable und clone (25 Punkte)
Betrachten Sie die Klassen List und Entry, mit denen der ADT Liste implementiert wurde.

komplettes Neuschreiben wäre ziemlich trivial, kein Grund vom gegebenen Code abzuweichen, nur paar Mal T statt Object

technisch möglich und vielleicht die größte machbare ‚Herausforderung‘ wäre eine eigene Klasse mit einem List-Object intern, alle Methodenaufrufe dahin weiterleiten


schade dass das Überschreiben nicht geht, spricht in Java-Logik etwas dagegen?
wenn man generische Klassen als Raw-Type benutzt, etwa naheliegend ArrayList, dann werden Methoden-Parameter & Co. ja auch zu Object, kommt aufs gleiche hinaus

es müssten sicher diverse Sonderregeln hinterlegt werden, aber das ist für Generics an sich eh 10fach der Fall,
lohnt wohl nicht die Mühe und Komplexität

Deprecated wäre noch eine leichte Variante, würde den falschen Gebrauch direkt anzeigen,
nahe am Ziel, den Aufrufer direkt zu informieren, aber ist eben keine Verhinderung, kein Compiler-Fehler

@SlaterB : Was du nach “-------” schreibst verstehe ich nicht

kurz steht da ‚wäre schön wenn es ginge‘ :wink: also das Vererben-Generic-machen

Sehe ich das richtig??? eine generische Liste die von List erbt ist nicht möglich und dein Fazit lautet ich soll den Code von List nehmen und einfach auf Generics ummodeln?

ich habe jetzt die typsicheren Klassen Entry und List geschrieben…

 * An Entry holds an Object <code>o</code> and a reference <code>next</code> to
 * the next Entry such that a linked List of Entry elements is generated.
 * 
 * 
 */
class Entry<T> {

   T object;
   Entry<T> next;

   Entry() {
      this(null, null);
   }

   Entry(T o) {
      this(o, null);
   }

   Entry(T o, Entry<T> e) {
      this.object = o;
      this.next = e;
   }

}

/**
 * A simple linked list. One may go through this list by {@link #advance()} until
 * the last position ({@link #endpos()}) is reached. One also may
 * {@link #delete()} and {@link #insert(Object)} elements. After advancing it is
 * possible to go back to the beginning by {@link #reset()}.
 * 
 * 
 * 
 */
public class List<T> implements Cloneable {

   /**
    * Reference on the first Entry of this List
    */
   private Entry<T> begin;
   /**
    * References before the actual Entry of this List
    */
   private Entry<T> pos;

   /**
    * Create a new empty List.
    */
   public List() {
      pos = begin = new Entry<T>();
   }

   /**
    * Determines if this List is empty or not.
    * 
    * @return <code>true</code>, if there are no elements in this List
    */
   public boolean empty() {
      return begin.next == null;
   }

   /**
    * Determines if it is possible to {@link #advance()} in this List. Returns
    * <code>true</code> if the last position of this List has been reached. An
    * {@link #empty()} List will alway deliver <code>true</code>
    * 
    * @return <code>true</code> if the last Entry in this List already has been
    *         reached.
    */
   public boolean endpos() { // true, wenn am Ende
      return pos.next == null;
   }

   /**
    * Returns to the beginning of this List.
    */
   public void reset() {
      pos = begin;
   }

   /**
    * Advances one step in this List.
    * 
    * @throws NoSuchElementException
    *            if the last Entry of this List already has been reached.
    */
   public void advance() {
      if (endpos()) {
         throw new NoSuchElementException("Already at the end of this List");
      }
      pos = pos.next;
   }

   /**
    * Returns the actual element of this List.
    * 
    * @return the actual element
    * 
    * @throws NoSuchElementException
    *            if the last Entry of this List already has been reached.
    */
   public Object elem() {
      if (endpos()) {
         throw new NoSuchElementException("Already at the end of this List");
      }
      return pos.next.object;
   }

   /**
    * Inserts <code>o</code> in this List. It will be placed before the actual
    * element. After insertion the inserted element will become the actual
    * element.
    * 
    * @param x
    *           the element to be inserted
    */
   public void add(T x) {
      Entry<T> newone = new Entry<T>(x, pos.next);

      pos.next = newone;
   }

   /**
    * Deletes the actual element of this List. The element after the actual
    * element will become the new actual element.
    * 
    * @throws NoSuchElementException
    *            if the last Entry of this List already has been reached.
    */
   public void delete() {
      if (endpos()) {
         throw new NoSuchElementException("Already at the end of this List");
      }
      pos.next = pos.next.next;
   }
   
   
   
   
   @Override
   public List<T> clone() {
	   
	   List<T> neueListe = new List<T>();
	   while(!this.endpos()) {
		   
		   neueListe.add((T) this.elem());
		   this.advance();
	   }
	   return neueListe;
   }
}```



meine Fragen dazu:

entspricht das jetzt einer "typsicheren" Liste?

habe ich diesen Aufgabenteil: "Implementieren Sie das Interface Cloneable in Ihrer generischen Liste." erfüllt?


mfg

So beim Drüberschauen: Ja. Du solltest aber auf jedem Fall eine kleine „ListTest“-Klasse erstellen, wo du die Funktionen alle mal aufrufst und überprüfst. Muss ja keine JUnit-Test sein … schadet aber auch nicht :smiley:

Zum „clone“ muss jemand anderes äußern. Ich habe darum bisher immer einen großen Bogen gemacht (und es auch praktisch nie wirklich gebraucht), weil es ziemlich frickelig sein kann, das richtig zu implementieren. Details dazu gibt es unter AngelikaLanger.com - Implementing the clone() Method - Part 1 - Angelika Langer Training/Consulting

frickelig ist wohl der richtige Ausdruck ^^

Ich muss einfach einmal dumm nachfragen: Ist mit clone() eine tiefe, flache oder keins von beiden (Kopie) gemeint?

T object; wird dann natürlich zu T t; oder bzw. T element; bzw. T elem; (bzw. E e and so on) und ist in privat-statisch-innerer Klasse deklariert/definiert(/initialisiert).

Echte Profis schauen einfach auf grepcode.c nach.

Je nachdem, ob dein Tutor dich mag, kannst du ihm das Blaue vom Himmel erzählen - oder Stillschweigen bewahren. :slight_smile: Mich mochte ein bisschen vor Jährchen niemand. :frowning:

Es handelt sich möglicherweise um einen Scherz - Cloneable hat ja gar keine Methoden.

By convention, classes that implement this interface should override Object.clone (which is protected) with a public method. See Object.clone() for details on overriding this method.

Hast du ja gemacht, aber ob deine Implementierung technisch ok ist - soll sich der Korrektor damit plagen.

[QUOTE=hamster1989]

   public Object elem() {
      if (endpos()) {
         throw new NoSuchElementException("Already at the end of this List");
      }
      return pos.next.object;
   }
```[/QUOTE]Würde den Rückgabe wert hier auch generisch machen: `public T elem()`.

das mit dem Rückgabetyp von elem() ist mir gestern auch schon aufgefallen, aber danke für den Hinweis!

der Code mit advance() und endPos() für Clonable ist ja ziemlich verrückt,
was ist wenn die Liste gerade ‚in der Mitte‘ war, wird dann nur die Hälfte kopiert?

und am Ende überläßt du die Liste auf endPos()? da freut sich ja der nächste…,
wenn überhaupt dann am Anfang + Ende reset(), aber auch das wäre schon schlimm genug,

Eingriff für evtl. gesetzten Stand jemand anderes,
alles wie nebenläufige Zugriffe kann man natürlich auch nicht bedenken

interne Implementierung nochmal nachzuprogrammieren ist auch nicht ganz schön,
aber eine Schleife von begin aus mit next ist kaum an Code und berührt sonst nix, nimmt nur die Daten mit

interessant noch die Frage, ob du die aktuelle pos kopieren willst, also dann mit Link auf entsprechendes Element in anderer Liste :wink:

Kannst du mir aufschreiben wie du das implementieren würdest?

meinst du ‚eine Schleife von begin aus mit next‘?
ganz gewiss nicht, wobei ich direkt gefragt sowieso keinerlei größeren individuellen Code poste :wink:

angesichts der Zeit die du hier schon investiert hast ist Abschreiben von fremden Lösungen doch eh wenig attraktiv,
willst du das nicht selber ausprobieren?

ob es sich für deine Abgabe eignet ist nicht sicher gesagt,
aber selbst unabhängig davon, wenn sich nun schon die Frage ergibt, ist das Code den du mit deiner Intelligenz ergründen solltest, wäre eine ideale Übung,

wenn es Fehler gibt dann hier Fragen stellen usw., kein Beinbruch, nicht Optimalweg A (alles direkt können) aber Weg B, auch gut


doch noch ein Tipp, weil so interessant:
du könntest übrigens deinen bisherigen clone-Code (grundsätzlich gut, falls von dir, genauso schwer)
ziemlich strukturiert dazu verwenden,
einfach an die Stelle jeder Methode den entsprechenden Methoden-Code einsetzen,
aber am Ende nicht mit dem pos-Attribut, damit dies nicht verändert wird, sondern einer lokalen Variable

Ich verstehe gerade ehrlich gesagt nichtmal welcher Teil von meinem Code noch fehlerhaft sein soll…

einzeilige Antworten auf jeweils lange Ausführungen von mir, das ärgert mich immer,

nun könnte ich nachfragen was du meinst, ob du dich auf meinen Satz ‚wenn es Fehler gibt dann hier Fragen stellen‘ beziehst,
der sich natürlich auf noch evtl. neuen Code bezog, nach dem du mich gerade erst noch gefragt hast usw., nicht deinen vorhandenen Code

oder ob dir doch nicht mehr so klar ist, was ich überhaupt an deinem bisherigen clone() kritisiert hatte um 12:35 usw.,

aber auch egal, ich bin schon verstimmt, und gedenke endlich wieder zu schweigen :wink: