John Squirrels
Seviye
San Francisco

Tip silme

grupta yayınlandı
MERHABA! Jenerikler üzerine ders serimize devam ediyoruz. Daha önce ne oldukları ve neden ihtiyaç duyuldukları hakkında genel bir fikrimiz vardı . Bugün jeneriklerin bazı özellikleri ve onlarla çalışma hakkında daha fazla şey öğreneceğiz. Hadi gidelim! Geçen dersteTip silme - 1 , jenerik tipler ile ham tipler arasındaki farktan bahsetmiştik . Ham tür, türü kaldırılmış genel bir sınıftır.
List list = new ArrayList();
İşte bir örnek. Burada, dosyamıza ne tür nesnelerin yerleştirileceğini belirtmiyoruz List. Böyle bir a oluşturup ona bazı nesneler eklemeye çalışırsak List, IDEA'da bir uyarı görürüz:
"Unchecked call to add(E) as a member of raw type of java.util.List".
Ancak jeneriklerin yalnızca Java 5'te göründüğü gerçeğinden de bahsettik. Bu sürüm yayınlandığında, programcılar zaten ham türleri kullanarak bir grup kod yazmıştı, bu nedenle dilin bu özelliği çalışmayı durduramadı ve yetenek Java'da ham türler oluşturma korunmuştur. Ancak sorunun daha yaygın olduğu ortaya çıktı. Bildiğiniz gibi, Java kodu bayt kodu adı verilen özel bir derlenmiş biçime dönüştürülür ve bu daha sonra Java sanal makinesi tarafından yürütülür. Ancak dönüştürme işlemi sırasında bayt koduna tür parametreleri hakkında bilgi koyarsak, Java 5'ten önce hiçbir tür parametresi olmadığı için önceden yazılmış tüm kodu bozar! Jenerik ilaçlarla çalışırken hatırlamanız gereken çok önemli bir kavram vardır. Tip silme denir. Bu, bir sınıfın bir tür parametresi hakkında bilgi içermediği anlamına gelir. Bu bilgi yalnızca derleme sırasında mevcuttur ve çalışma zamanından önce silinir (erişilemez hale gelir). Dosyanıza yanlış türde nesne koymaya çalışırsanız List<String>, derleyici bir hata üretecektir. Bu, dilin yaratıcılarının jenerikler oluşturduklarında tam olarak elde etmek istedikleri şeydir: derleme zamanı kontrolleri. Ancak tüm Java kodunuz bayt koduna dönüştüğünde, artık tür parametreleri hakkında bilgi içermez. Bayt kodunda, List<Cat>kedi listeniz dizelerden farklı değildir List<String>. Bayt kodunda, hiçbir şey bunun catsbir nesne listesi olduğunu söylemez Cat. Bu tür bilgiler derleme sırasında silinir — yalnızca bir listeniz olduğu gerçeği List<Object> catsprogramın bayt kodunda sona erer. Bunun nasıl çalıştığını görelim:
public class TestClass<T> {

   private T value1;
   private T value2;

   public void printValues() {
       System.out.println(value1);
       System.out.println(value2);
   }

   public static <T> TestClass<T> createAndAdd2Values(Object o1, Object o2) {
       TestClass<T> result = new TestClass<>();
       result.value1 = (T) o1;
       result.value2 = (T) o2;
       return result;
   }

   public static void main(String[] args) {
       Double d = 22.111;
       String s = "Test String";
       TestClass<Integer> test = createAndAdd2Values(d, s);
       test.printValues();
   }
}
Kendi jenerik sınıfımızı oluşturduk TestClass. Oldukça basit: Aslında, nesne oluşturulduğunda hemen depolanan 2 nesneden oluşan küçük bir "koleksiyon". 2 alanı vardır T. Yöntem createAndAdd2Values()yürütüldüğünde, iki iletilen nesne ( Object ave Object btüre dönüştürülmeli Tve sonra nesneye eklenmelidir TestClass. Yöntemde main(), bir tür argümanı oluşturuyoruz TestClass<Integer>, yani Integertür argümanı, tür parametresinin yerini alıyor . Ayrıca a ve a'yı Integerda geçiyoruz. Yöntem.Programımızın çalışacağını düşünüyor musunuz?Sonuçta type argümanını belirtmiştik ama a kesinlikle an'a dönüştürülemez !DoubleStringcreateAndAdd2Values()IntegerStringIntegermain()yöntem ve kontrol. Konsol çıktısı:
22.111
Test String
Bu beklenmedikti! Bu neden oldu? Tip silme işleminin sonucudur. IntegerNesnemizi başlatmak için kullanılan tür bağımsız değişkeni hakkındaki bilgiler, TestClass<Integer> testkod derlendiğinde silindi. alan olur TestClass<Object> test. Bizim Doubleve Stringbağımsız değişkenlerimiz kolayca nesnelere dönüştürüldü Object(beklediğimiz gibi nesnelere dönüştürülmediler Integer!) ve sessizce TestClass. İşte başka bir basit ama çok açıklayıcı tip silme örneği:
import java.util.ArrayList;
import java.util.List;

public class Main {

   private class Cat {

   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       List<Integer> numbers = new ArrayList<>();
       List<Cat> cats = new ArrayList<>();

       System.out.println(strings.getClass() == numbers.getClass());
       System.out.println(numbers.getClass() == cats.getClass());

   }
}
Konsol çıktısı:
true
true
Görünüşe göre üç farklı argüman türüyle koleksiyonlar oluşturduk - String, Integerve kendi sınıfımız Cat. Ancak bayt koduna dönüştürme sırasında, üç liste de olur List<Object>, bu nedenle program çalıştığında bize her üç durumda da aynı sınıfı kullandığımızı söyler.

Diziler ve jeneriklerle çalışırken silme yazın

Diziler ve genel sınıflarla (örneğin ) çalışırken açıkça anlaşılması gereken çok önemli bir nokta vardır List. Programınız için veri yapılarını seçerken de dikkate almalısınız. Jenerikler tip silmeye tabidir. Tür parametreleri hakkında bilgi çalışma zamanında mevcut değildir. Buna karşılık diziler, program çalışırken veri türlerini bilir ve bu tür bilgileri kullanabilir. Bir diziye geçersiz bir tür koymaya çalışmak, bir istisnanın atılmasına neden olur:
public class Main2 {

   public static void main(String[] args) {

       Object x[] = new String[3];
       x[0] = new Integer(222);
   }
}
Konsol çıktısı:
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
Diziler ve jenerikler arasında çok büyük bir fark olduğu için uyumluluk sorunları olabilir. Her şeyden önce, bir jenerik nesne dizisi veya hatta yalnızca parametreleştirilmiş bir dizi oluşturamazsınız. Bu biraz kafa karıştırıcı mı geliyor? Hadi bir bakalım. Örneğin, Java'da bunların hiçbirini yapamazsınız:
new List<T>[]
new List<String>[]
new T[]
Bir nesne dizisi oluşturmaya çalışırsak List<String>, genel dizi oluşturma hakkında şikayet eden bir derleme hatası alırız:
import java.util.List;

public class Main2 {

   public static void main(String[] args) {

       // Compilation error! Generic array creation
       List<String>[] stringLists = new List<String>[1];
   }
}
Ama bu neden yapılır? Bu tür dizilerin oluşturulmasına neden izin verilmiyor? Bunların hepsi tip güvenliğini sağlamak içindir. Derleyici bu tür genel nesne dizileri oluşturmamıza izin verirse, kendimiz için bir ton sorun yaratabiliriz. İşte Joshua Bloch'un "Etkili Java" kitabından basit bir örnek:
public static void main(String[] args) {

   List<String>[] stringLists = new List<String>[1];  //  (1)
   List<Integer> intList = Arrays.asList(42, 65, 44);  //  (2)
   Object[] objects = stringLists;  //  (3)
   objects[0] = intList;  //  (4)
   String s = stringLists[0].get(0);  //  (5)
}
List<String>[] stringListsGibi bir dizi oluşturmaya izin verildiğini ve bir derleme hatası oluşturmayacağını düşünelim . Bu doğruysa, yapabileceğimiz bazı şeyler şunlardır: 1. satırda, bir dizi liste oluşturuyoruz: List<String>[] stringLists. Dizimiz bir tane içeriyor List<String>. 2. satırda, bir sayı listesi oluşturuyoruz: List<Integer>. 3. satırda, bizim List<String>[]bir Object[] objectsdeğişkene atarız. Java dili buna izin verir: bir dizi nesne , tüm alt sınıfların nesnelerini ve nesnelerini Xdepolayabilir . Buna göre, herhangi bir şeyi bir diziye koyabilirsiniz . 4. satırda, dizinin tek elemanını (a ) a ile değiştiriyoruz . Böylece, yalnızca saklaması amaçlanan bir diziye a koyduk.XXObjectobjects()List<String>List<Integer>List<Integer>List<String>nesneler! Sadece 5. satırı çalıştırdığımızda bir hata ile karşılaşacağız. ClassCastExceptionÇalışma zamanında A atılacak. Buna göre Java'ya bu tür dizilerin oluşturulmasına yönelik bir yasak eklendi. Bu, bu tür durumlardan kaçınmamızı sağlar.

Tip silme işleminin etrafından nasıl dolaşabilirim?

Tip silmeyi öğrendik. Sistemi kandırmaya çalışalım! :) Görev: Jenerik bir sınıfımız var TestClass<T>. createNewT()Bu sınıf için yeni bir nesne yaratacak ve döndürecek bir metot yazmak istiyoruz T. Ama bu imkansız, değil mi? Derleme sırasında türle ilgili tüm bilgiler Tsilinir ve çalışma zamanında ne tür bir nesne oluşturmamız gerektiğini belirleyemeyiz. Aslında bunu yapmanın zor bir yolu var. Muhtemelen Java'nın bir sınıfı olduğunu hatırlıyorsunuzdur Class. Herhangi bir nesnemizin sınıfını belirlemek için kullanabiliriz:
public class Main2 {

   public static void main(String[] args) {

       Class classInt = Integer.class;
       Class classString = String.class;

       System.out.println(classInt);
       System.out.println(classString);
   }
}
Konsol çıktısı:
class java.lang.Integer
class java.lang.String
Ama burada bahsetmediğimiz bir yön var. Oracle belgelerinde, Class sınıfının jenerik olduğunu göreceksiniz! Tip silme - 3

https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html

Belgeler, "T - bu Class nesnesi tarafından modellenen sınıfın türü" diyor. Bunu belgeleme dilinden düz konuşmaya çevirirsek, nesnenin sınıfının Integer.classsadece değil Class, daha çok olduğunu anlarız Class<Integer>. Nesnenin türü String.classyalnızca değil Class, bunun yerine Class<String>, vb. Hala net değilse, önceki örneğe bir tür parametresi eklemeyi deneyin:
public class Main2 {

   public static void main(String[] args) {

       Class<Integer> classInt = Integer.class;
       // Compilation error!
       Class<String> classInt2 = Integer.class;


       Class<String> classString = String.class;
       // Compilation error!
       Class<Double> classString2 = String.class;
   }
}
Ve şimdi, bu bilgiyi kullanarak, tip silmeyi atlayabilir ve görevimizi başarabiliriz! Bir tür parametresi hakkında bilgi almaya çalışalım. Tip argümanımız şöyle olacaktır MySecretClass:
public class MySecretClass {

   public MySecretClass() {

       System.out.println("A MySecretClass object was created successfully!");
   }
}
Çözümümüzü pratikte şu şekilde kullanıyoruz:
public class TestClass<T> {

   Class<T> typeParameterClass;

   public TestClass(Class<T> typeParameterClass) {
       this.typeParameterClass = typeParameterClass;
   }

   public T createNewT() throws IllegalAccessException, InstantiationException {
       T t = typeParameterClass.newInstance();
       return t;
   }

   public static void main(String[] args) throws InstantiationException, IllegalAccessException {

       TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
       MySecretClass secret = testString.createNewT();

   }
}
Konsol çıktısı:
A MySecretClass object was created successfully!
Gerekli sınıf bağımsız değişkenini genel sınıfımızın yapıcısına ilettik:
TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
Bu, tür bağımsız değişkeni hakkındaki bilgileri kaydetmemize izin vererek, tamamen silinmesini engelledi. Sonuç olarak, bir tane oluşturabildik.Tnesne! :) Bununla birlikte bugünkü dersimiz de sona eriyor. Jeneriklerle çalışırken tip silmeyi her zaman hatırlamanız gerekir. Bu geçici çözüm pek uygun görünmüyor, ancak jeneriklerin Java oluşturulduğunda Java dilinin bir parçası olmadığını anlamalısınız. Parametreli koleksiyonlar oluşturmamıza ve derleme sırasında hataları yakalamamıza yardımcı olan bu özellik daha sonra ele alındı. İlk sürümdeki jenerikleri içeren diğer bazı dillerde, tür silme yoktur (örneğin, C#'ta). Bu arada, jenerikleri incelemeyi bitirmedik! Bir sonraki derste, jeneriklerin birkaç özelliğini daha tanıyacaksınız. Şimdilik, birkaç görevi çözmek iyi olur! :)
Yorumlar
  • Popüler
  • Yeni
  • Eskimiş
Yorum bırakmak için giriş yapmalısınız
Bu sayfada henüz yorum yok