హాయ్! జెనరిక్స్ గురించి మన అధ్యయనాన్ని కొనసాగిద్దాం. మీరు ఇప్పటికే మునుపటి పాఠాల నుండి వాటి గురించి గణనీయమైన జ్ఞానాన్ని పొందారు ( జనరిక్స్తో పనిచేసేటప్పుడు మరియు టైప్ ఎరేజర్ గురించి varargs ఉపయోగించడం గురించి ), కానీ మేము ఇంకా పరిగణించని ఒక ముఖ్యమైన అంశం ఉంది — వైల్డ్కార్డ్లు . ఇది జెనరిక్స్ యొక్క చాలా ముఖ్యమైన లక్షణం. ఎంతగా అంటే మేము దానికి ప్రత్యేక పాఠాన్ని అంకితం చేసాము! వైల్డ్కార్డ్ల గురించి ప్రత్యేకంగా సంక్లిష్టంగా ఏమీ లేదు. మీరు వెంటనే చూస్తారు :)
ఒక ఉదాహరణ చూద్దాం:

public class Main {
public static void main(String[] args) {
String str = new String("Test!");
// No problem
Object obj = str;
List<String> strings = new ArrayList<String>();
// Compilation error!
List<Object> objects = strings;
}
}
ఏమి జరుగుతుంది ఇక్కడ? మేము రెండు సారూప్య పరిస్థితులను చూస్తాము. సందర్భంలో, మేము String
ఒక వస్తువుకు ఒక వస్తువును ప్రసారం చేస్తాము Object
. ఇక్కడ ఎటువంటి సమస్యలు లేవు - ప్రతిదీ ఊహించిన విధంగా పని చేస్తుంది. కానీ రెండవ పరిస్థితిలో, కంపైలర్ లోపాన్ని సృష్టిస్తుంది. కానీ మేము అదే పని చేస్తున్నాము, కాదా? ఈసారి మేము అనేక వస్తువుల సేకరణను ఉపయోగిస్తున్నాము. కానీ లోపం ఎందుకు సంభవిస్తుంది? తేడా ఏమిటి? String
మనం ఒక వస్తువును Object
లేదా 20 వస్తువులకు ప్రసారం చేస్తున్నామా ? ఒక వస్తువు మరియు వస్తువుల సేకరణ మధ్య ఒక ముఖ్యమైన వ్యత్యాసం ఉంది . తరగతి తరగతికి చెందిన పిల్లవాడు అయితే , . B
A
Collection<B>
Collection<A>
List<String>
ఈ కారణంగానే మేము ఎకి ప్రసారం చేయలేకపోయాముList<Object>
. String
యొక్క బిడ్డ Object
, కానీ List<String>
బిడ్డ కాదు List<Object>
. ఇది చాలా సహజంగా అనిపించకపోవచ్చు. భాషా సృష్టికర్తలు ఈ విధంగా ఎందుకు చేశారు? కంపైలర్ మనకు లోపాన్ని ఇవ్వలేదని ఊహించుదాం:
List<String> strings = new ArrayList<String>();
List<Object> objects = strings;
ఈ సందర్భంలో, ఉదాహరణకు, మేము ఈ క్రింది వాటిని చేయవచ్చు:
objects.add(new Object());
String s = strings.get(0);
List<Object>
కంపైలర్ మాకు ఎటువంటి లోపాన్ని అందించనందున మరియు సూచించే సూచనను సృష్టించడానికి మమ్మల్ని అనుమతించినందున , మేము సేకరణకు strings
ఏదైనా పాత వస్తువును జోడించవచ్చు ! అందువల్ల, మా సేకరణలో సాధారణ రకం ఆహ్వానంలో టైప్ ఆర్గ్యుమెంట్ ద్వారా పేర్కొన్న వస్తువులు మాత్రమే ఉన్నాయని మేము హామీని కోల్పోయాము . ఇతర మాటలలో, మేము జెనరిక్స్ యొక్క ప్రధాన ప్రయోజనాన్ని కోల్పోయాము — రకం భద్రత. మరియు కంపైలర్ దీన్ని చేయకుండా మమ్మల్ని ఆపలేదు కాబట్టి, రన్ టైమ్లో మాత్రమే మనకు ఎర్రర్ వస్తుంది, ఇది ఎల్లప్పుడూ కంపైలేషన్ ఎర్రర్ కంటే చాలా ఘోరంగా ఉంటుంది. ఇలాంటి పరిస్థితులను నివారించడానికి, కంపైలర్ మాకు లోపాన్ని ఇస్తుంది: Object
strings
String
// Compilation error
List<Object> objects = strings;
... మరియు అది List<String>
వంశస్థుడు కాదని మనకు గుర్తు చేస్తుంది List<Object>
. ఇది జెనరిక్స్ కోసం ఒక ఐరన్క్లాడ్ నియమం మరియు వారితో పనిచేసేటప్పుడు ఇది తప్పనిసరిగా గుర్తుంచుకోవాలి. ముందుకు వెళ్దాం. మనకు చిన్న తరగతి సోపానక్రమం ఉందని అనుకుందాం:
public class Animal {
public void feed() {
System.out.println("Animal.feed()");
}
}
public class Pet extends Animal {
public void call() {
System.out.println("Pet.call()");
}
}
public class Cat extends Pet {
public void meow() {
System.out.println("Cat.meow()");
}
}
పెంపుడు జంతువు ద్వారా వారసత్వంగా పొందిన సాధారణ జంతు తరగతి ద్వారా సోపానక్రమం అగ్రస్థానంలో ఉంది. పెంపుడు జంతువుకు 2 ఉపవర్గాలు ఉన్నాయి: కుక్క మరియు పిల్లి. iterateAnimals()
ఇప్పుడు మనం ఒక సాధారణ పద్ధతిని సృష్టించాల్సిన అవసరం ఉందని అనుకుందాం . పద్ధతి ఏదైనా జంతువుల సేకరణను తీసుకోవాలి ( Animal
, Pet
, Cat
, Dog
), అన్ని మూలకాలపై మళ్ళించాలి మరియు ప్రతి పునరావృత సమయంలో కన్సోల్లో సందేశాన్ని ప్రదర్శించాలి. అటువంటి పద్ధతిని వ్రాయడానికి ప్రయత్నిద్దాం:
public static void iterateAnimals(Collection<Animal> animals) {
for(Animal animal: animals) {
System.out.println("Another iteration in the loop!");
}
}
సమస్య పరిష్కారమైనట్లే! అయితే, మేము ఇటీవల తెలుసుకున్నట్లుగా, List<Cat>
, List<Dog>
మరియు List<Pet>
వారసులు కాదు List<Animal>
! iterateAnimals()
దీని అర్థం మేము పిల్లుల జాబితాతో పద్ధతిని కాల్ చేయడానికి ప్రయత్నించినప్పుడు , మనకు సంకలన లోపం వస్తుంది:
import java.util.*;
public class Main3 {
public static void iterateAnimals(Collection<Animal> animals) {
for(Animal animal: animals) {
System.out.println("Another iteration in the loop!");
}
}
public static void main(String[] args) {
List<Cat> cats = new ArrayList<>();
cats.add(new Cat());
cats.add(new Cat());
cats.add(new Cat());
cats.add(new Cat());
// Compilation error!
iterateAnimals(cats);
}
}
పరిస్థితి మాకు అంత బాగా లేదు! ప్రతి రకమైన జంతువులను లెక్కించడానికి మనం వేర్వేరు పద్ధతులను వ్రాయాలా? అసలైన, లేదు, మేము చేయము :) మరియు ఇది జరిగినప్పుడు, వైల్డ్కార్డ్లు మాకు సహాయం చేస్తాయి! కింది నిర్మాణాన్ని ఉపయోగించి మేము ఒక సాధారణ పద్ధతితో సమస్యను పరిష్కరించవచ్చు:
public static void iterateAnimals(Collection<? extends Animal> animals) {
for(Animal animal: animals) {
System.out.println("Another iteration in the loop!");
}
}
ఇది వైల్డ్ కార్డ్. మరింత ఖచ్చితంగా, ఇది అనేక రకాల వైల్డ్కార్డ్లలో మొదటిది. ఇది ఎగువ సరిహద్దు వైల్డ్కార్డ్లుగా పిలువబడుతుంది మరియు దీని ద్వారా వ్యక్తీకరించబడింది ? విస్తరించింది . ఈ నిర్మాణం మనకు ఏమి చెబుతుంది? దీనర్థం, పద్ధతి (? విస్తరించి ఉన్న జంతువు) Animal
నుండి వచ్చిన ఏదైనా తరగతి వస్తువుల సేకరణ లేదా వస్తువుల సేకరణను అంగీకరిస్తుంది. మరో మాటలో చెప్పాలంటే, పద్ధతి , , , లేదా ఆబ్జెక్ట్ల Animal
సేకరణను అంగీకరించగలదు - దీనికి ఎటువంటి తేడా లేదు. ఇది పని చేస్తుందని మనల్ని మనం ఒప్పించుకుందాం: Animal
Pet
Dog
Cat
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Animal());
animals.add(new Animal());
List<Pet> pets = new ArrayList<>();
pets.add(new Pet());
pets.add(new Pet());
List<Cat> cats = new ArrayList<>();
cats.add(new Cat());
cats.add(new Cat());
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
iterateAnimals(animals);
iterateAnimals(pets);
iterateAnimals(cats);
iterateAnimals(dogs);
}
కన్సోల్ అవుట్పుట్:
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
మేము మొత్తం 4 సేకరణలు మరియు 8 ఆబ్జెక్ట్లను సృష్టించాము మరియు కన్సోల్లో సరిగ్గా 8 ఎంట్రీలు ఉన్నాయి. ప్రతిదీ గొప్పగా పనిచేస్తుంది! :) వైల్డ్కార్డ్ మాకు నిర్దిష్ట రకాలతో ముడిపడి ఉన్న అవసరమైన లాజిక్ను ఒకే పద్ధతిలో సులభంగా అమర్చడానికి అనుమతించింది. ప్రతి రకమైన జంతువులకు ప్రత్యేక పద్ధతిని వ్రాయవలసిన అవసరాన్ని మేము తొలగించాము. మన అప్లికేషన్ను జూ లేదా వెటర్నరీ ఆఫీస్ ఉపయోగిస్తే మనకు ఎన్ని పద్ధతులు అవసరమో ఊహించుకోండి :) కానీ ఇప్పుడు వేరే పరిస్థితిని చూద్దాం. మా వారసత్వ సోపానక్రమం మారదు: అత్యున్నత స్థాయి తరగతి Animal
, Pet
కేవలం దిగువ తరగతి మరియు తదుపరి స్థాయిలో Cat
మరియు తరగతులు. Dog
ఇప్పుడు మీరు పద్ధతిని తిరిగి వ్రాయాలి, iterateAnimals()
తద్వారా కుక్కలు మినహా ఏ రకమైన జంతువుతోనైనా పని చేయండి . అంటే, అది అంగీకరించాలి Collection<Animal>
,Collection<Pet>
లేదా Collection<Car>
, కానీ అది పని చేయకూడదు Collection<Dog>
. మనం దీన్ని ఎలా సాధించగలం? ప్రతి రకానికి ఒక ప్రత్యేక పద్ధతిని వ్రాసే అవకాశాన్ని మనం మళ్లీ ఎదుర్కొంటున్నట్లు అనిపిస్తుంది :/ మనం ఏమి జరగాలనుకుంటున్నామో కంపైలర్కు ఎలా వివరించాలి? ఇది నిజానికి చాలా సులభం! మరోసారి, వైల్డ్కార్డ్లు ఇక్కడ మా సహాయానికి వస్తాయి. కానీ ఈసారి మేము మరొక రకమైన వైల్డ్కార్డ్ని ఉపయోగిస్తాము — తక్కువ-బౌండెడ్ వైల్డ్కార్డ్ , ఇది సూపర్ ఉపయోగించి వ్యక్తీకరించబడుతుంది .
public static void iterateAnimals(Collection<? super Cat> animals) {
for(int i = 0; i < animals.size(); i++) {
System.out.println("Another iteration in the loop!");
}
}
ఇక్కడ సూత్రం సమానంగా ఉంటుంది. ఆబ్జెక్ట్ల సముదాయాన్ని ఇన్పుట్గా లేదా తరగతికి చెందిన ఏదైనా పూర్వీకులను ఇన్పుట్గా ఈ పద్ధతి అంగీకరించవచ్చని నిర్మాణం <? super Cat>
కంపైలర్కు చెబుతుంది . ఈ సందర్భంలో, క్లాస్, దాని పేరెంట్ , మరియు దాని పేరెంట్ యొక్క పేరెంట్, , అన్నీ ఈ వివరణకు సరిపోతాయి. తరగతి మా పరిమితితో సరిపోలడం లేదు, కాబట్టి ఆర్గ్యుమెంట్తో పద్ధతిని ఉపయోగించే ప్రయత్నం సంకలన దోషానికి దారి తీస్తుంది: iterateAnimals()
Cat
Cat
Cat
Pet
Animal
Dog
List<Dog>
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Animal());
animals.add(new Animal());
List<Pet> pets = new ArrayList<>();
pets.add(new Pet());
pets.add(new Pet());
List<Cat> cats = new ArrayList<>();
cats.add(new Cat());
cats.add(new Cat());
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
iterateAnimals(animals);
iterateAnimals(pets);
iterateAnimals(cats);
// Compilation error!
iterateAnimals(dogs);
}
మేము మా సమస్యను పరిష్కరించాము మరియు మరోసారి వైల్డ్కార్డ్లు చాలా ఉపయోగకరంగా మారాయి :) దీనితో, పాఠం ముగిసింది. మీ జావా అధ్యయనంలో జెనరిక్స్ ఎంత ముఖ్యమైనవో ఇప్పుడు మీరు చూశారు — మేము వాటి గురించి 4 మొత్తం పాఠాలను కలిగి ఉన్నాము! కానీ ఇప్పుడు మీరు టాపిక్లో బాగా ప్రావీణ్యం కలిగి ఉన్నారు మరియు మీరు ఉద్యోగ ఇంటర్వ్యూలలో మీ నైపుణ్యాలను నిరూపించుకోవచ్చు :) మరియు ఇప్పుడు, పనులను తిరిగి పొందే సమయం వచ్చింది! మీ అధ్యయనాలలో ఉత్తమ విజయం! :)
GO TO FULL VERSION