8 Ekim 2018 Pazartesi

Java Interface Casting - Sınıfı Arayüze veya Arayüzü Sınıfa Cast Etme

Giriş
Java
- bir sınıfı bir arayüze cast etmeye
- veya bir arayüzü bir sınıfa cast etmeye

derleme esnasında izin veriyor ve hata olsa bile derleme hatası vermiyor. Aslında derleme esnasında hata yakalamaması gıcık bir şey.

Derleme Hatası Yoksa Bile Halen ClassCastException Fırlatılır
Elimizde şöyle bir kod olsun. Bu kod derlense bile çalışma esnasında halen ClassCastException fırlatır.
List<String> strList = new ArrayList<>();
Date d = (Date) strList;
1. Arayüzü Sınıfa Cast Etmek Neden Lazım
Bu mümkün ve açıklaması şöyle.
"A narrowing reference conversion exists from reference type S to reference type T if all of the following are true:"
...
5) "S is an interface type, T is a class type, and T does not name a final class."
Sınıflar bazen hem bir ata sınıftan kalıtırlar hem de bir veya daha çok arayüzü gerçekleştirirler ve bu nesne kodda arayüz olarak kullanılır. Dolayısıyla arayüzü sınıfa cast etme ihtiyacı doğar.

Örnek
Elimizde şöyle bir kod olsun.
public class SneakyListDate extends Date implements List<Foo> {
    ...
}
Şöyle yaparız. Burada arayüz gerçek sınıfa cast ediliyor.
List<Foo> list = new SneakyListDate();
Date date = (Date) list; // This one is valid, compiles and runs just fine
2. Sınıfı Arayüze Cast Etme
Örnek
Şöyle yaparız. Burada sınıf arayüze cast ediliyor. Derleyici bu cast'lerde hata olsa bile yakalamaz. Ancak sınıf bir başka sınıfa cast edilirse bunu yakalar.
public class Foo {

  public List<String> getCollectionCast() {
    return (List<String>) this;    // No compiler error
  }

  public Map<String, String> getCollection2Cast() {
    return (Map<String, String>) this;    // No compiler error
  }

  public Other getCast() {
    return (Other)this;     // Incompatible types. Cannot cast Foo to Other
  }

  public  static class Other {
    // Just for casting demo
  }

}
Açıklaması şöyle. Çünkü Foo'dan kalıtan bir sınıf List arayüzünden de kalıtabilir. Bu durumda getCollection() metodunu doğru hale gelir.
It's not because they're collection classes, it's because they're interfaces. Foo doesn't implement them, but subclasses of it could. So it's not a compile-time error, since those methods may be valid for subclasses. At runtime, if this isn't of a class that implements those interfaces, naturally it's a runtime error.

If you change List<String> to ArrayList<String>, you'll get a compiler-time error for that, too, since a Foo subclass could implement List, but can't extend ArrayList (since Foo doesn't). Similarly, if you make Foo final, the compiler will give you an error for your interface casts because it knows they can never be true (since Foo can't have subclasses, and doesn't implement those interfaces).
Örnek
Eğer nesne final ise derleyici nesneyi bir arayüze çevirirken gerekli kontrolleri yapar. Elimizde şöyle bir kod olsun.
interface I1 { }
interface I2 { }
class C1 implements I1 { }
class C2 implements I2 { }

public class Test{
  public static void main(){
    C1 o1 = new C1();
    C2 o2 = new C2();
    Integer o3 = new Integer(4);

    I2 x = (I2)o1; //compiler does not complain
    I2 y = (I2)o3; //compiler complains here !!
  }
}
Açıklaması şöyle. Burada Integer'dan kalıtan bir başka sınıf olmadığını derleyici biliyor. Dolayısıyla I2'den kalıtma ihtimali yok.
The Integer class is final, so o3 cannot be an instance of a subclass of Integer

Hiç yorum yok:

Yorum Gönder