హాయ్! మేము జెనరిక్స్పై మా పాఠాల శ్రేణిని కొనసాగిస్తాము. అవి ఏమిటో మరియు అవి ఎందుకు అవసరమో మాకు ఇంతకుముందు సాధారణ ఆలోచన వచ్చింది. ఈ రోజు మనం జెనరిక్స్ యొక్క కొన్ని లక్షణాల గురించి మరియు వాటితో పని చేయడం గురించి మరింత తెలుసుకుందాం. వెళ్దాం! గత పాఠంలో
, మేము సాధారణ రకాలు మరియు ముడి రకాలు మధ్య వ్యత్యాసం గురించి మాట్లాడాము . ముడి రకం అనేది సాధారణ తరగతి, దీని రకం తీసివేయబడింది.
డాక్యుమెంటేషన్ ఇలా చెబుతోంది, "T - ఈ తరగతి వస్తువు ద్వారా రూపొందించబడిన తరగతి రకం." దీన్ని డాక్యుమెంటేషన్ భాష నుండి సాదా ప్రసంగానికి అనువదించడం, ఆబ్జెక్ట్ యొక్క తరగతి
![ఎరేజర్ రకం - 1](https://cdn.codegym.cc/images/article/339f5100-8248-4d1b-8fef-c11c9fbc21bc/800.jpeg)
List list = new ArrayList();
ఇక్కడ ఒక ఉదాహరణ ఉంది. మనలో ఏ రకమైన వస్తువులు ఉంచబడతాయో ఇక్కడ మేము సూచించము List
. List
మేము అలాంటి వాటిని సృష్టించి , దానికి కొన్ని వస్తువులను జోడించడానికి ప్రయత్నిస్తే , మేము IDEAలో హెచ్చరికను చూస్తాము:
"Unchecked call to add(E) as a member of raw type of java.util.List".
కానీ మేము జావా 5లో మాత్రమే జెనరిక్స్ కనిపించిన వాస్తవం గురించి కూడా మాట్లాడాము. ఈ సంస్కరణ విడుదలయ్యే సమయానికి, ప్రోగ్రామర్లు ఇప్పటికే ముడి రకాలను ఉపయోగించి అనేక కోడ్లను వ్రాశారు, కాబట్టి భాష యొక్క ఈ లక్షణం పని చేయడాన్ని ఆపలేదు మరియు దాని సామర్థ్యం జావాలో ముడి రకాలను సృష్టించడం భద్రపరచబడింది. అయితే, సమస్య మరింత విస్తృతంగా మారింది. మీకు తెలిసినట్లుగా, జావా కోడ్ బైట్కోడ్ అని పిలువబడే ప్రత్యేక కంపైల్డ్ ఫార్మాట్కి మార్చబడుతుంది, అది జావా వర్చువల్ మెషీన్ ద్వారా అమలు చేయబడుతుంది. కానీ మార్పిడి ప్రక్రియలో మేము టైప్ పారామితుల గురించి సమాచారాన్ని బైట్కోడ్లో ఉంచినట్లయితే, అది గతంలో వ్రాసిన కోడ్ను విచ్ఛిన్నం చేస్తుంది, ఎందుకంటే జావా 5కి ముందు టైప్ పారామితులు లేవు! జెనరిక్స్తో పని చేస్తున్నప్పుడు, మీరు గుర్తుంచుకోవలసిన ముఖ్యమైన అంశం ఒకటి ఉంది. దీనిని టైప్ ఎరేజర్ అంటారు. రకం పరామితి గురించి తరగతిలో ఎటువంటి సమాచారం లేదని దీని అర్థం. ఈ సమాచారం సంకలనం సమయంలో మాత్రమే అందుబాటులో ఉంటుంది మరియు రన్టైమ్కు ముందు తొలగించబడుతుంది (అసాధ్యం అవుతుంది). మీరు మీలో తప్పు రకం వస్తువును ఉంచడానికి ప్రయత్నిస్తే List<String>
, కంపైలర్ లోపాన్ని సృష్టిస్తుంది. భాషా సృష్టికర్తలు జెనరిక్స్ని సృష్టించినప్పుడు ఇదే సాధించాలనుకుంటున్నారు: కంపైల్-టైమ్ చెక్లు. కానీ మీ జావా కోడ్ మొత్తం బైట్కోడ్గా మారినప్పుడు, అది ఇకపై టైప్ పారామితుల గురించి సమాచారాన్ని కలిగి ఉండదు. బైట్కోడ్లో, మీ పిల్లుల జాబితా స్ట్రింగ్ల List<Cat>
కంటే భిన్నంగా లేదు . List<String>
బైట్కోడ్లో, వస్తువుల cats
జాబితా అని ఏమీ చెప్పలేదు Cat
. అటువంటి సమాచారం సంకలనం సమయంలో తొలగించబడుతుంది - మీరు జాబితాను కలిగి ఉన్నారనే వాస్తవం మాత్రమే List<Object> cats
ప్రోగ్రామ్ యొక్క బైట్కోడ్లో ముగుస్తుంది. ఇది ఎలా పని చేస్తుందో చూద్దాం:
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();
}
}
మేము మా స్వంత సాధారణ TestClass
తరగతిని సృష్టించాము. ఇది చాలా సులభం: ఇది వాస్తవానికి 2 వస్తువుల యొక్క చిన్న "సేకరణ", ఇది వస్తువు సృష్టించబడిన వెంటనే నిల్వ చేయబడుతుంది. దీనికి 2 T
ఫీల్డ్లు ఉన్నాయి. పద్ధతిని అమలు చేసినప్పుడు createAndAdd2Values()
, రెండు పాస్ చేయబడిన ఆబ్జెక్ట్లు ( Object a
మరియు Object b
తప్పనిసరిగా రకానికి ప్రసారం చేసి T
, ఆపై TestClass
ఆబ్జెక్ట్కి జోడించబడాలి. పద్ధతిలో main()
, మేము ఒక క్రియేట్ చేస్తాము TestClass<Integer>
, అనగా Integer
టైప్ ఆర్గ్యుమెంట్ టైప్ పారామీటర్ను భర్తీ చేస్తుంది . మేము a మరియు a to Integer
కూడా పాస్ చేస్తున్నాము పద్ధతి . మా ప్రోగ్రామ్ పని చేస్తుందని మీరు అనుకుంటున్నారా? అన్నింటికంటే, మేము టైప్ ఆర్గ్యుమెంట్గా పేర్కొన్నాము, కానీ ఖచ్చితంగా ఒక కు ప్రసారం చేయడం సాధ్యం కాదు !Double
String
createAndAdd2Values()
Integer
String
Integer
main()
పద్ధతి మరియు తనిఖీ. కన్సోల్ అవుట్పుట్:
22.111
Test String
అది ఊహించనిది! ఇలా ఎందుకు జరిగింది? ఇది రకం ఎరేజర్ యొక్క ఫలితం. కోడ్ కంపైల్ చేయబడినప్పుడు Integer
మా ఆబ్జెక్ట్ను ఇన్స్టాంటియేట్ చేయడానికి ఉపయోగించే టైప్ ఆర్గ్యుమెంట్ గురించిన సమాచారం తొలగించబడింది. TestClass<Integer> test
ఫీల్డ్ అవుతుంది TestClass<Object> test
. మా Double
మరియు String
వాదనలు సులభంగా వస్తువులుగా మార్చబడ్డాయి Object
(అవి మనం ఊహించిన విధంగా వస్తువులుగా మార్చబడవు Integer
!) మరియు నిశ్శబ్దంగా కు జోడించబడ్డాయి TestClass
. రకం ఎరేజర్ యొక్క మరొక సాధారణ కానీ చాలా బహిర్గతం చేసే ఉదాహరణ ఇక్కడ ఉంది:
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());
}
}
కన్సోల్ అవుట్పుట్:
true
true
మేము మూడు విభిన్న రకాల ఆర్గ్యుమెంట్లతో సేకరణలను సృష్టించినట్లు కనిపిస్తోంది — String
, Integer
, మరియు మా స్వంత Cat
తరగతి. కానీ బైట్కోడ్కి మార్చే సమయంలో, మూడు జాబితాలు మారతాయి List<Object>
, కాబట్టి ప్రోగ్రామ్ రన్ అయినప్పుడు మనం మూడు సందర్భాల్లోనూ ఒకే తరగతిని ఉపయోగిస్తున్నామని చెబుతుంది.
శ్రేణులు మరియు జెనరిక్స్తో పని చేస్తున్నప్పుడు ఎరేజర్ని టైప్ చేయండి
శ్రేణులు మరియు సాధారణ తరగతులతో (ఉదాహరణకు) పని చేస్తున్నప్పుడు స్పష్టంగా అర్థం చేసుకోవలసిన ముఖ్యమైన అంశం ఉందిList
. మీ ప్రోగ్రామ్ కోసం డేటా స్ట్రక్చర్లను ఎంచుకునేటప్పుడు కూడా మీరు దానిని పరిగణనలోకి తీసుకోవాలి. జెనరిక్స్ రకం ఎరేజర్కు లోబడి ఉంటాయి. రన్టైమ్లో టైప్ పారామీటర్ల గురించి సమాచారం అందుబాటులో లేదు. దీనికి విరుద్ధంగా, ప్రోగ్రామ్ రన్ అవుతున్నప్పుడు శ్రేణులకు వాటి డేటా రకం గురించి తెలుసు మరియు దాని గురించిన సమాచారాన్ని ఉపయోగించవచ్చు. శ్రేణిలో చెల్లని రకాన్ని ఉంచడానికి ప్రయత్నిస్తే మినహాయింపు విసిరివేయబడుతుంది:
public class Main2 {
public static void main(String[] args) {
Object x[] = new String[3];
x[0] = new Integer(222);
}
}
కన్సోల్ అవుట్పుట్:
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
శ్రేణులు మరియు జెనరిక్స్ మధ్య చాలా పెద్ద వ్యత్యాసం ఉన్నందున, వాటికి అనుకూలత సమస్యలు ఉండవచ్చు. అన్నింటికంటే మించి, మీరు సాధారణ వస్తువుల శ్రేణిని లేదా కేవలం పారామితి చేయబడిన శ్రేణిని కూడా సృష్టించలేరు. అది కాస్త గందరగోళంగా అనిపిస్తుందా? ఒకసారి చూద్దాము. ఉదాహరణకు, మీరు జావాలో వీటిలో దేనినీ చేయలేరు:
new List<T>[]
new List<String>[]
new T[]
మేము ఆబ్జెక్ట్ల శ్రేణిని సృష్టించడానికి ప్రయత్నిస్తే List<String>
, సాధారణ శ్రేణి సృష్టి గురించి ఫిర్యాదు చేసే సంకలన లోపం మనకు వస్తుంది:
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];
}
}
అయితే ఇది ఎందుకు జరుగుతుంది? అటువంటి శ్రేణుల సృష్టి ఎందుకు అనుమతించబడదు? ఇదంతా రకం భద్రతను అందించడానికి. కంపైలర్ అటువంటి జెనరిక్ ఆబ్జెక్ట్ల శ్రేణులను సృష్టించడానికి వీలు కల్పిస్తే, మనమే టన్ను సమస్యలను సృష్టించుకోవచ్చు. జాషువా బ్లాచ్ పుస్తకం "ఎఫెక్టివ్ జావా" నుండి ఇక్కడ ఒక సాధారణ ఉదాహరణ:
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>[] stringLists
అటువంటి శ్రేణిని సృష్టించడం అనుమతించబడిందని మరియు సంకలన దోషాన్ని సృష్టించదని ఊహించండి . ఇది నిజమైతే, మనం చేయగలిగే కొన్ని విషయాలు ఇక్కడ ఉన్నాయి: లైన్ 1లో, మేము జాబితాల శ్రేణిని సృష్టిస్తాము: List<String>[] stringLists
. మా శ్రేణిలో ఒకటి ఉంది List<String>
. లైన్ 2 లో, మేము సంఖ్యల జాబితాను సృష్టిస్తాము: List<Integer>
. 3వ పంక్తిలో, మనము List<String>[]
వేరియబుల్కు కేటాయిస్తాము Object[] objects
. జావా భాష దీనిని అనుమతిస్తుంది: వస్తువుల శ్రేణి అన్ని సబ్క్లాస్ల వస్తువులు మరియు వస్తువులను X
నిల్వ చేయగలదు . దీని ప్రకారం, మీరు శ్రేణిలో ఏదైనా ఉంచవచ్చు . లైన్ 4లో, మేము శ్రేణి (a ) యొక్క ఏకైక మూలకాన్ని a తో భర్తీ చేస్తాము . అందువల్ల, మేము నిల్వ చేయడానికి మాత్రమే ఉద్దేశించిన శ్రేణిలో ఉంచాముX
X
Object
objects()
List<String>
List<Integer>
List<Integer>
List<String>
వస్తువులు! మేము లైన్ 5ని అమలు చేసినప్పుడు మాత్రమే లోపాన్ని ఎదుర్కొంటాము. A ClassCastException
రన్టైమ్లో విసిరివేయబడుతుంది. దీని ప్రకారం, అటువంటి శ్రేణుల సృష్టిపై నిషేధం జావాకు జోడించబడింది. ఇది అలాంటి పరిస్థితులను నివారించవచ్చు.
నేను టైప్ ఎరేజర్ని ఎలా పొందగలను?
బాగా, మేము టైప్ ఎరేజర్ గురించి తెలుసుకున్నాము. వ్యవస్థను మోసగించడానికి ప్రయత్నిద్దాం! :) టాస్క్: మాకు సాధారణTestClass<T>
తరగతి ఉంది. createNewT()
మేము ఈ తరగతికి ఒక కొత్త ఆబ్జెక్ట్ని సృష్టించి, తిరిగి ఇచ్చే పద్ధతిని వ్రాయాలనుకుంటున్నాము T
. కానీ ఇది అసాధ్యం, సరియైనదా? సంకలనం సమయంలో రకం గురించిన మొత్తం సమాచారం T
తొలగించబడుతుంది మరియు రన్టైమ్లో మనం ఏ రకమైన వస్తువును సృష్టించాలో నిర్ణయించలేము. దీన్ని చేయడానికి నిజానికి ఒక గమ్మత్తైన మార్గం ఉంది. జావాకు క్లాస్ ఉందని మీరు బహుశా గుర్తుంచుకోవచ్చు Class
. మన వస్తువులలో ఏదైనా తరగతిని నిర్ణయించడానికి మేము దీన్ని ఉపయోగించవచ్చు:
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);
}
}
కన్సోల్ అవుట్పుట్:
class java.lang.Integer
class java.lang.String
అయితే ఇక్కడ మనం మాట్లాడని అంశం ఒకటి ఉంది. ఒరాకిల్ డాక్యుమెంటేషన్లో, క్లాస్ క్లాస్ జెనరిక్ అని మీరు చూస్తారు! ![ఎరేజర్ రకం - 3](https://cdn.codegym.cc/images/article/231dc03d-3204-4227-ba6d-603c191c6092/800.jpeg)
https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html
Integer.class
కేవలం కాదు Class
, కానీ Class<Integer>
. ఆబ్జెక్ట్ రకం String.class
కేవలం కాదు Class
, కానీ Class<String>
, మొదలైనవి. ఇది ఇప్పటికీ స్పష్టంగా లేకుంటే, మునుపటి ఉదాహరణకి టైప్ పరామితిని జోడించి ప్రయత్నించండి:
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;
}
}
ఇప్పుడు, ఈ జ్ఞానాన్ని ఉపయోగించి, మేము టైప్ ఎరేజర్ను దాటవేయవచ్చు మరియు మా పనిని సాధించవచ్చు! రకం పరామితి గురించి సమాచారాన్ని పొందడానికి ప్రయత్నిద్దాం. మా రకం వాదన ఇలా ఉంటుంది MySecretClass
:
public class MySecretClass {
public MySecretClass() {
System.out.println("A MySecretClass object was created successfully!");
}
}
మరియు ఇక్కడ మేము మా పరిష్కారాన్ని ఆచరణలో ఎలా ఉపయోగిస్తాము:
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();
}
}
కన్సోల్ అవుట్పుట్:
A MySecretClass object was created successfully!
మేము మా సాధారణ తరగతి యొక్క కన్స్ట్రక్టర్కు అవసరమైన క్లాస్ ఆర్గ్యుమెంట్ని పంపాము:
TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
ఇది టైప్ ఆర్గ్యుమెంట్ గురించి సమాచారాన్ని సేవ్ చేయడానికి మమ్మల్ని అనుమతించింది, ఇది పూర్తిగా తొలగించబడకుండా నిరోధించింది. ఫలితంగా, మేము ఒక సృష్టించగలిగాముT
వస్తువు! :) దాంతో ఈరోజు పాఠం ముగిసిపోతుంది. జెనరిక్స్తో పని చేస్తున్నప్పుడు మీరు ఎల్లప్పుడూ టైప్ ఎరేజర్ని గుర్తుంచుకోవాలి. ఈ ప్రత్యామ్నాయం చాలా సౌకర్యవంతంగా కనిపించడం లేదు, కానీ జెనరిక్స్ సృష్టించబడినప్పుడు జావా భాషలో భాగం కాదని మీరు అర్థం చేసుకోవాలి. పారామితి చేయబడిన సేకరణలను రూపొందించడంలో మరియు సంకలనం సమయంలో లోపాలను గుర్తించడంలో మాకు సహాయపడే ఈ ఫీచర్, తర్వాత పరిష్కరించబడింది. మొదటి వెర్షన్ నుండి జెనరిక్స్ని చేర్చిన కొన్ని ఇతర భాషలలో, టైప్ ఎరేజర్ లేదు (ఉదాహరణకు, C#లో). మార్గం ద్వారా, మేము జెనరిక్స్ అధ్యయనం పూర్తి చేయలేదు! తదుపరి పాఠంలో, మీరు జెనరిక్స్ యొక్క మరికొన్ని లక్షణాలతో పరిచయం పొందుతారు. ప్రస్తుతానికి, రెండు పనులను పరిష్కరించడం మంచిది! :)