CodeGym /Blog Jawa /Acak /Java Generics: carane nggunakake kurung angled ing laku
John Squirrels
tingkat
San Francisco

Java Generics: carane nggunakake kurung angled ing laku

Diterbitake ing grup

Pambuka

Diwiwiti karo JSE 5.0, generik ditambahake menyang arsenal basa Jawa.

Apa generik ing basa Jawa?

Generik minangka mekanisme khusus Jawa kanggo ngleksanakake pemrograman umum — cara kanggo njlèntrèhaké data lan algoritma sing ngidini sampeyan nggarap jinis data sing béda tanpa ngganti katrangan saka algoritma kasebut. Situs web Oracle duwe tutorial kapisah khusus kanggo generik: " Pelajaran ". Kanggo mangerteni generik, sampeyan kudu ngerti sebabe perlu lan apa sing diwenehake. Bagian " Napa Gunakake Generik? " Tutorial kasebut ujar manawa sawetara tujuan luwih kuat kanggo mriksa jinis nalika nyusun wektu lan ngilangi kabutuhan cast sing jelas. Generik ing Jawa: carane nggunakake kurung siku ing laku - 1Ayo nyiyapake sawetara tes ing kompiler java online Tutorialspoint sing dikasihi . Upaminipun sampeyan duwe kode ing ngisor iki:

import java.util.*;
public class HelloWorld {
	public static void main(String []args) {
		List list = new ArrayList();
		list.add("Hello");
		String text = list.get(0) + ", world!";
		System.out.print(text);
	}
}
Kode iki bakal mlaku kanthi apik. Nanging apa yen boss teka kanggo kita lan ngandika sing "Hello, donya!" minangka tembung sing ora digunakake lan sampeyan mung kudu bali "Halo"? Kita bakal mbusak kode sing nggabungake ", donya!" Iki katon ora mbebayani, ta? Nanging kita bener-bener entuk kesalahan AT COMPILE TIME:

error: incompatible types: Object cannot be converted to String
Masalahe yaiku ing dhaptar kita nyimpen Obyek. String minangka turunan saka Obyek (amarga kabeh kelas Jawa kanthi implisit marisi Obyek ), tegese kita butuh pemeran eksplisit, nanging ora nambah. Sajrone operasi concatenation, String.valueOf(obj) cara statis bakal disebut nggunakake obyek. Pungkasan, bakal nelpon metode toString kelas Obyek . Ing tembung liyane, Daftar kita ngemot Obyek . Iki tegese ing ngendi wae kita butuh jinis tartamtu (ora Obyek ), kita kudu nindakake konversi jinis dhewe:

import java.util.*;
public class HelloWorld {
	public static void main(String []args) {
		List list = new ArrayList();
		list.add("Hello!");
		list.add(123);
		for (Object str : list) {
		    System.out.println("-" + (String)str);
		}
	}
}
Nanging, ing kasus iki, amarga List njupuk obyek, bisa nyimpen ora mung String s, nanging uga Integer s. Nanging sing paling awon yaiku kompiler ora weruh apa-apa sing salah ing kene. Lan saiki kita bakal entuk kesalahan AT RUN TIME (dikenal minangka "kesalahan runtime"). Kesalahan bakal dadi:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Sampeyan kudu setuju yen iki ora apik banget. Lan kabeh iki amarga compiler dudu intelijen buatan sing bisa ngira kanthi bener maksud programmer. Java SE 5 ngenalake generik supaya kita ngandhani kompiler babagan tujuane - babagan jinis apa sing bakal digunakake. Kita ndandani kode kanthi ngandhani kompiler apa sing dikarepake:

import java.util.*;
public class HelloWorld {
	public static void main(String []args) {
		List<String> list = new ArrayList<>();
		list.add("Hello!");
		list.add(123);
		for (Object str : list) {
		    System.out.println("-" + str);
		}
	}
}
Nalika sampeyan bisa ndeleng, kita ora perlu maneh cast kanggo String a . Kajaba iku, kita duwe kurung sudut sing ngubengi argumen jinis. Saiki kompiler ora ngidini kita ngumpulake kelas nganti mbusak baris sing nambahake 123 menyang dhaptar, amarga iki minangka Integer . Lan bakal ngandhani kita. Akeh wong nyebut generik "gula sintaksis". Lan padha bener, amarga sawise generik dikompilasi, padha pancene dadi jinis konversi padha. Ayo ndeleng bytecode saka kelas sing dikompilasi: sing nggunakake cast eksplisit lan sing nggunakake generik: Generik ing Jawa: carane nggunakake kurung siku ing laku - 2Sawise kompilasi, kabeh generik bakal dibusak. Iki diarani " type erasure". Tipe erasure lan generik dirancang kanggo kompatibel mundur karo versi lawas saka JDK nalika bebarengan ngidini compiler kanggo bantuan karo definisi jinis ing versi anyar saka Jawa.

Jinis mentahan

Ngomong babagan generik, kita mesthi duwe rong kategori: jinis parameter lan jinis mentah. Jinis mentah yaiku jinis sing ngilangi "klarifikasi jinis" ing kurung sudut: Generik ing Jawa: carane nggunakake kurung siku ing laku - 3Jinis parameter, ing tangan, kalebu "klarifikasi": Generik ing Jawa: carane nggunakake kurung siku ing laku - 4Nalika sampeyan bisa ndeleng, kita nggunakake konstruksi sing ora biasa, ditandhani panah ing gambar. Iki minangka sintaksis khusus sing ditambahake ing Java SE 7. Iki diarani " berlian ". Kenging punapa? Kurung sudut mbentuk inten: <> . Sampeyan uga kudu ngerti yen sintaks berlian digandhengake karo konsep " inferensi tipe ". Sawise kabeh, compiler, ndeleng <>ing sisih tengen, katon ing sisih kiwa operator assignment, ngendi iku nemokake jinis variabel kang Nilai lagi diutus. Adhedhasar apa sing ditemokake ing bagean iki, ngerti jinis nilai ing sisih tengen. Nyatane, yen jinis umum diwenehi ing sisih kiwa, nanging ora ing sisih tengen, kompiler bisa nyimpulake jinis kasebut:

import java.util.*;
public class HelloWorld {
	public static void main(String []args) {
		List<String> list = new ArrayList();
		list.add("Hello, World");
		String data = list.get(0);
		System.out.println(data);
	}
}
Nanging iki nyampur gaya anyar karo generik lan gaya lawas tanpa. Lan iki banget undesirable. Nalika nyusun kode ing ndhuwur, kita entuk pesen ing ngisor iki:

Note: HelloWorld.java uses unchecked or unsafe operations
Nyatane, alesan kenapa sampeyan kudu nambah berlian ing kene katon ora bisa dingerteni. Nanging iki contone:

import java.util.*;
public class HelloWorld {
	public static void main(String []args) {
		List<String> list = Arrays.asList("Hello", "World");
		List<Integer> data = new ArrayList(list);
		Integer intNumber = data.get(0);
		System.out.println(data);
	}
}
Sampeyan bakal kelingan yen ArrayList duwe konstruktor kapindho sing njupuk koleksi minangka argumen. Lan ing kene ana sing didhelikake sing jahat. Tanpa sintaks berlian, kompiler ora ngerti yen lagi diapusi. Kanthi sintaks berlian, ora. Dadi, Aturan #1 yaiku: tansah gunakake sintaks berlian kanthi jinis parameter. Yen ora, kita duwe risiko ilang ing ngendi kita nggunakake jinis mentah. Kanggo ngilangi peringatan "nggunakake operasi sing ora dicenthang utawa ora aman", kita bisa nggunakake anotasi @SuppressWarnings ("ora dicenthang") ing metode utawa kelas. Nanging pikirake kenapa sampeyan mutusake nggunakake. Elinga aturan nomer siji. Mungkin sampeyan kudu nambah argumen jinis.

Metode Generik Jawa

Generik ngidini sampeyan nggawe metode sing jinis parameter lan jinis bali wis diparameter. Bagean sing kapisah dikhususake kanggo kemampuan iki ing tutorial Oracle: " Metode Umum ". Penting kanggo ngelingi sintaks sing diwulangake ing tutorial iki:
  • kalebu dhaptar paramèter jinis ing kurung sudut;
  • dhaptar paramèter jinis dadi sadurunge jinis bali cara kang.
Ayo katon ing conto:

import java.util.*;
public class HelloWorld {
	
    public static class Util {
        public static <T> T getValue(Object obj, Class<T> clazz) {
            return (T) obj;
        }
        public static <T> T getValue(Object obj) {
            return (T) obj;
        }
    }

    public static void main(String []args) {
		List list = Arrays.asList("Author", "Book");
		for (Object element : list) {
		    String data = Util.getValue(element, String.class);
		    System.out.println(data);
		    System.out.println(Util.<String>getValue(element));
		}
    }
}
Yen sampeyan ndeleng kelas Util , sampeyan bakal weruh manawa ana rong cara umum. Thanks kanggo kemungkinan jinis inferensi, kita bisa nuduhake jinis langsung menyang kompiler, utawa bisa nemtokake dhewe. Loro-lorone opsi diwenehi ing conto. Miturut cara, sintaksis ndadekake akeh pangertèn yen sampeyan mikir babagan. Nalika ngumumake metode umum, kita nemtokake parameter jinis sadurunge metode kasebut, amarga yen kita ngumumake parameter jinis sawise metode kasebut, JVM ora bakal bisa nemtokake jinis sing bakal digunakake. Patut, kita pisanan ngumumake yen kita bakal nggunakake parameter jinis T , lan banjur ujar manawa bakal ngasilake jinis iki. Mesthi, Util.<Integer>getValue(elemen, String.class) bakal gagal kanthi kesalahan:jinis ora kompatibel: Class<String> ora bisa diowahi kanggo Class<Integer> . Nalika nggunakake cara umum, sampeyan kudu tansah ngelingi jinis erasure. Ayo katon ing conto:

import java.util.*;
public class HelloWorld {
	
    public static class Util {
        public static <T> T getValue(Object obj) {
            return (T) obj;
        }
    }

    public static void main(String []args) {
		List list = Arrays.asList(2, 3);
		for (Object element : list) {
		    System.out.println(Util.<Integer>getValue(element) + 1);
		}
    }
}
Iki bakal mlaku kanthi apik. Nanging mung anggere compiler mangerténi sing jinis bali saka cara disebut Integer . Ganti statement output console karo baris ing ngisor iki:

System.out.println(Util.getValue(element) + 1);
Kita entuk kesalahan:

bad operand types for binary operator '+', first type: Object, second type: int.
Ing tembung liyane, jinis erasure wis kedadeyan. Compiler ndeleng manawa ora ana sing nemtokake jinis kasebut, mula jinis kasebut dituduhake minangka Obyek lan metode kasebut gagal kanthi kesalahan.

Kelas umum

Ora mung cara sing bisa diparameter. Kelas uga bisa. Bagian "Jenis Umum" saka tutorial Oracle dikhususake kanggo iki. Ayo nimbang conto:

public static class SomeType<T> {
	public <E> void test(Collection<E> collection) {
		for (E element : collection) {
			System.out.println(element);
		}
	}
	public void test(List<Integer> collection) {
		for (Integer element : collection) {
			System.out.println(element);
		}
	}
}
Kabeh iku prasaja ing kene. Yen kita nggunakake kelas umum, parameter jinis dituduhake sawise jeneng kelas. Saiki ayo nggawe conto kelas iki ing metode utama :

public static void main(String []args) {
	SomeType<String> st = new SomeType<>();
	List<String> list = Arrays.asList("test");
	st.test(list);
}
Kode iki bakal mlaku kanthi apik. Compiler weruh sing ana Dhaptar nomer lan Koleksi Strings . Nanging apa yen kita ngilangi parameter jinis lan nindakake iki:

SomeType st = new SomeType();
List<String> list = Arrays.asList("test");
st.test(list);
Kita entuk kesalahan:

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
Maneh, iki jinis erasure. Amarga kelas kasebut ora nggunakake parameter jinis maneh, kompiler mutusake yen, amarga kita ngliwati List , metode karo List<Integer> paling cocok. Lan kita gagal karo kesalahan. Mulane, kita duwe Aturan #2: Yen sampeyan duwe kelas umum, mesthi nemtokake parameter jinis.

Watesan

Kita bisa mbatesi jinis sing ditemtokake ing metode umum lan kelas. Contone, umpamane kita pengin wadhah mung nampa Nomer minangka argumen jinis. Fitur iki diterangake ing bagean Parameter Tipe Wates ing tutorial Oracle. Ayo katon ing conto:

import java.util.*;
public class HelloWorld {
	
    public static class NumberContainer<T extends Number> {
        private T number;
    
        public NumberContainer(T number) { this.number = number; }
    
        public void print() {
            System.out.println(number);
        }
    }

    public static void main(String []args) {
		NumberContainer number1 = new NumberContainer(2L);
		NumberContainer number2 = new NumberContainer(1);
		NumberContainer number3 = new NumberContainer("f");
    }
}
Nalika sampeyan bisa ndeleng, kita wis diwatesi parameter jinis kanggo nomer kelas / antarmuka utawa turunane. Elinga yen sampeyan bisa nemtokake ora mung kelas, nanging uga antarmuka. Tuladhane:

public static class NumberContainer<T extends Number & Comparable> {
Generik uga ndhukung wildcard Dipérang dadi telung jinis: Panggunaan wildcard kudu netepi prinsip Get-Put . Bisa diterangake kaya ing ngisor iki:
  • Gunakake wildcard ngluwihi nalika sampeyan mung entuk nilai saka struktur.
  • Gunakake wildcard super nalika sampeyan mung sijine nilai menyang struktur.
  • Lan aja nggunakake wildcard nalika sampeyan pengin njaluk lan sijine saka / kanggo struktur.
Prinsip iki uga diarani prinsip Producer Extends Consumer Super (PECS). Iki minangka conto cilik saka kode sumber kanggo metode Java Collections.copy : Generik ing Jawa: carane nggunakake kurung siku ing laku - 5Lan ing ngisor iki ana conto sing ora bakal bisa digunakake:

public static class TestClass {
	public static void print(List<? extends String> list) {
		list.add("Hello, World!");
		System.out.println(list.get(0));
	}
}

public static void main(String []args) {
	List<String> list = new ArrayList<>();
	TestClass.print(list);
}
Nanging yen sampeyan ngganti ngluwihi karo super , banjur kabeh iku apik. Amarga kita isi dhaptar kanthi nilai sadurunge nampilake isine, iku konsumen . Mulane, kita nggunakake super.

pusaka

Generik duwe fitur menarik liyane: warisan. Cara kerjane warisan kanggo generik diterangake ing " Generik, Warisan, lan Subtipe " ing tutorial Oracle. Sing penting yaiku ngelingi lan ngerteni ing ngisor iki. Kita ora bisa nindakake iki:

List<CharSequence> list1 = new ArrayList<String>();
Amarga warisan tumindak beda karo generik: Generik ing Jawa: carane nggunakake kurung siku ing laku - 6Lan iki conto liyane sing bakal gagal karo kesalahan:

List<String> list1 = new ArrayList<>();
List<Object> list2 = list1;
Maneh, kabeh prasaja ing kene. List<String> dudu turunan saka List<Object> , sanajan String minangka turunan saka Object . Kanggo nguatake apa sing sampeyan sinau, disaranake sampeyan nonton video pelajaran saka Kursus Jawa

Kesimpulan

Dadi, kita wis nyegerake memori babagan generik. Yen sampeyan arang banget njupuk kauntungan saka kemampuane, sawetara rincian dadi kabur. Mugi review singkat iki wis mbantu jogging memori sampeyan. Kanggo asil sing luwih apik, aku menehi saran supaya sampeyan sinau babagan materi ing ngisor iki:
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION