ClassCastException bei Verwendung eines Interfaces

Ploflo82

Mitglied
Hallo Leute,

ich habe mich zur Übung daran versucht, eine Sortierer-Klasse zu implementieren, deren Methoden (bubbleSort, mergeSort, etc) jeweils ein Array eines eigenen Interfaces 'Vergleichbar' entgegennehmen. Beim Testen ist mir dann eine ClassCastException um die Ohren geflogen, die ich nicht ganz nachvollziehen kann. Ein minimalistisches Code-Beispiel (hat nichts mehr mit dem Sortieren zu tun) findet ihr unten.
Ich befürchte, ich habe das Prinzip der Polymorphie falsch verstanden...
Code:
Exception in thread "main" java.lang.ClassCastException: [LIntRFace; cannot be cast to [LIntRFaceImplementer;
        at ClassCastExceptionTest.<init>(ClassCastExceptionTest.java:6)
        at ClassCastExceptionTest.main(ClassCastExceptionTest.java:17)

Java:
class ClassCastExceptionTest {

      public ClassCastExceptionTest() {
        IntRFaceImplementer [] array = new IntRFaceImplementer[1];
        array[0] = new IntRFaceImplementer(42);
        array = (IntRFaceImplementer []) doSomething(array);     
      }
      
      public IntRFace [] doSomething(IntRFace [] arr) {
             IntRFace [] ret = new IntRFace[1];
             ret[0] = arr[0];
             return ret;
      }


      public static void main(String [] args) {
        new ClassCastExceptionTest();
      }

}

interface IntRFace {
  public int getX(IntRFace other);
}

class IntRFaceImplementer implements IntRFace {

      private int x;
      public IntRFaceImplementer(int z) {
        x = z;
      }
      
      public int getX(IntRFace other) {
        return ((IntRFaceImplementer) other).x;
      }

}

Warum verliert das IntRFaceImplementer-Objekt im IntRFace-Array seinen Typ, wenn es in ein neues Array umgespeichert wird? Ist genau dafür nicht die Polymorphie von Java gut?
Hoffe, es kann mir jemand helfen 🙂
LG,
Ploflo82
 
Zuletzt bearbeitet:
Das Array ist vom Typ IntRFace. Deswegen kannst du es nicht zu einem IntRFaceImplementer-Array casten. Es ist nunmal nicht vom Typ IntRFaceImplementer[].

Das das Element im Array vom Typ IntRFaceImplementer ist ist völlig egal, darum geht es nicht.

Auf der anderen Seite aber kannst du das Element im Array ohne Probleme nach IntRFaceImplementer casten.
 
Polymorphie ist eine schöne Sache, aber da kommt der Graus, der in Java alles kaputt macht, auch die dann später eingeführten Generics:
ARRAYS, buuuuhuuuu, man graut sich vor ihnen

ein IntRFace[] kannst du nicht auf IntRFaceImplementer[] casten, das [L in der Fehlermeldung steht für die Arrays

in diesem Fall durchaus zu Recht, in ein IntRFace[] könnte theoretisch auch ein Objekt einer ganz anderen Klasse, die das Interface implementiert, gespeichert werden,

was machst du dann mit dem Array als IntRFaceImplementer[] gesehen?

gefährlicher ist es andersrum, wie beim Parameter, das genauere Array kann auf ein allgemeines gecastet werden,
Folgen siehe hier:
Java:
public class Test2
{ 
    public static void main(String[] args) {
        String[] a = new String[1];
        Object[] b = a;
        b[0] = b; // ArrayStoreException, ganz unschön und zum Glück selten
    }
}

mit Listen und Generics geht vieles leichter

folgendes könntest du noch nutzen:
Java:
IntRFace [] ret = (IntRFace[]) Array.newInstance(arr[0].getClass(),1);
erzeugt in diesem Fall wiederum ein IntRFaceImplementer[]
 
Der Code an sich ist ja schon "kaputt"..
Code:
  public int getX(IntRFace other) {
        return ((IntRFaceImplementer) other).x;
      }
Wie kannst du dich darauf verlassen, dass "other" vom Typ IntRFaceImplementer ist?
 
Wenn ich das ganze ohne Array mache funktioniert es aber (siehe folgenden Code):

Java:
class ClassCastExceptionTest2 {

      public ClassCastExceptionTest2() {
        IntRFaceImplementer impl = new IntRFaceImplementer(42);
        impl = (IntRFaceImplementer) doSomething(impl);
        System.out.println(impl.getXSquared());
      }

      public IntRFace doSomething(IntRFace intrf) {
             IntRFace ret = intrf;
             return ret;
      }


      public static void main(String [] args) {
        new ClassCastExceptionTest2();
      }

}

interface IntRFace {
  public int getX(IntRFace other);
}

class IntRFaceImplementer implements IntRFace {

      private int x;
      public IntRFaceImplementer(int z) {
        x = z;
      }

      public int getX(IntRFace other) {
        return ((IntRFaceImplementer) other).x;
      }
      
      public int getXSquared() {
        return x*x;
      }

}

Mir ist klar, dass in Zeile 10 nur der Verweis auf das Objekt übergeben wird und keine Kopie des Objekts angelegt wird, aber warum wird nicht dasselbe auch in meiner ersten Version mit dem Array so gemacht?
 
hast du die Antworten nicht gelesen?
hast du mein Beispiel mit der ArrayStoreException nicht nachvollzogen?

ohne Denken musst du die Situation akzeptieren wie sie ist,
wenn du Denken willst, dann steht schon alles da, eine Wiederholung bringt auch nix
(Fragen dazu sind natürlich immer möglich)
 
Zuletzt bearbeitet von einem Moderator:
[...] ein IntRFace[] kannst du nicht auf IntRFaceImplementer[] casten, das [L in der Fehlermeldung steht für die Arrays
in diesem Fall durchaus zu Recht, in ein IntRFace[] könnte theoretisch auch ein Objekt einer ganz anderen Klasse, die das Interface implementiert, gespeichert werden,[...]

Es könnte theoretisch sein, ja. Dann würde ich die Exception auch akzeptieren (muss ich natürlich sowieso 😉 ), aber in diesem Fall ist in dem Array ja tatsächlich ein IntRFaceImplementer-Objekt, weswegen ich der Ansicht bin bzw. war, dass ein Typecast funktionieren müsste.

was machst du dann mit dem Array als IntRFaceImplementer[] gesehen?

Den Satz hab ich in der Tat nicht verstanden... Was meinst du damit?
 
> Den Satz hab ich in der Tat nicht verstanden... Was meinst du damit?

das ist so allgemein gesprochen, ein 20Tonner LKW fährt auf, was machst du dann mit deiner Wattebrücke über den Fluss?

> aber in diesem Fall ist in dem Array ja tatsächlich ein IntRFaceImplementer-Objekt

da die andere Richtung, String[] als Object[] verwenden, auch Probleme macht und erlaubt ist,
kann man argumentieren, dass der Compiler das durchlassen könnte, ja,

aber ist nicht nicht der Fall

gut oder schlecht kann das jeder finden wie er/ sie will 😉
besser 1/2 Fällen verhindert als alles offen vs. Behinderung in 1/2 Fällen
 

Zurück
Oben