ජාවා ජෙනරික්ස් වයිල්ඩ්කාඩ්ස් | Java Generics Wildcards (? extends, ? super, ?) | SC Guide

ආයුබෝවන් කට්ටිය! කොහොමද ඉතින්? Java වලින් code කරන අපි හැමෝටම නිතරම වගේ මුහුණ දෙන්න වෙන පොඩි "අයියෝ, මේක මොකක්ද?" වගේ ප්රශ්නයක් තමයි Generics
කියන්නේ. විශේෂයෙන්ම, ලොකු Applications හදනකොට, මේක නැතුව බෑ. නමුත්, සමහර වෙලාවට Generics
එක්ක වැඩ කරනකොට, "Type Safety" තියාගන්න ඕනෙ නිසා, Code එක ටිකක් Rigidity එකට යනවා නේද? අන්න ඒ වගේ තැනකට අපිට එන "Super Hero" කෙනෙක් තමයි Wildcards
කියන්නේ. මේ Article එකෙන් අපි Java වල තියෙන මේ Wildcards
කියන නියම Concept එක ගැන සරලව, පහසුවෙන් තේරුම් ගනිමු.
Generics කියන්නේ මොකක්ද?
මුලින්ම අපි බලමු Generics
කියන්නේ මොකක්ද කියලා. සරලවම කිව්වොත්, Generics
කියන්නේ Java වලට ආපු නියම feature එකක්. මේකෙන් වෙන්නේ Class
, Interface
, Methods
වලට "Type Parameters" දාන්න පුළුවන් වෙන එක. හිතන්නකෝ, අපිට List
එකක් ඕනේ String
විතරක් තියාගන්න. එතකොට අපි List<String>
කියලා දානවා. මේකෙන් වෙන්නේ Compile Time එකේදී ම Type Safety එක හදන එක. ඒ කියන්නේ, ඔයා අත්වැරදීමකින් List<String>
එකකට Integer
එකක් දාන්න හැදුවොත්, Compiler එකෙන් Error එකක් දෙනවා. මේකෙන් Run Time එකේදී ClassCastException
වගේ ඒවයින් ගැලවෙන්න පුළුවන්. ඒ වගේම, Code Reuseability එකත් වැඩි වෙනවා. එකම Code එකකින් විවිධ Data Types එක්ක වැඩ කරන්න පුළුවන්. වැඩේ පහසුයි නේද?
Wildcards මොකටද?
හරි, එහෙනම් Generics
හොඳයි. හැබැයි මේකේ පොඩි ගැටලුවක් තියෙනවා. හිතන්නකෝ, ඔයාට Method එකක් තියෙනවා List<Number>
එකක් Parameter එකක් විදියට ගන්න. ඔයා හිතයි, List<Integer>
එකක් මේ Method එකට දෙන්න පුළුවන් කියලා. මොකද Integer
කියන්නේ Number
එකක්නේ. නමුත් Java වලදී, List<Integer>
කියන්නේ List<Number>
එකක Subtype එකක් නෙවෙයි! මේක ගොඩක් දෙනෙක්ට පැටලෙන තැනක්.
Java මේක කරන්නේ Type Safety එක වෙනුවෙන්. හිතන්න, List<Number>
එකකට Integer
, Double
, Float
වගේ ඕනෑම Number
Type එකක් දාන්න පුළුවන්. දැන් ඔයා List<Integer>
එකක් List<Number>
එකක් විදියට දීලා, ඒ List එකට Double
එකක් දැම්මොත්, Original List<Integer>
එකට Double
එකක් Add වෙනවා. ඒක බරපතල Type Safety Issue එකක්. අන්න මේ ගැටලුවට විසඳුම තමයි Wildcards
.
Unbounded Wildcards (?) - බඳින්නේ නැති ඒවා
මුලින්ම අපි බලමු Unbounded Wildcard
එක ගැන. මේක අපි ?
කියලා දානවා. මේකෙන් කියවෙන්නේ "ඕනෑම වර්ගයක" (any type) කියන එක. මේක ගොඩක් වෙලාවට පාවිච්චි කරන්නේ Generic Type
එක ගැන වැඩිය ගණන් නොගෙන Elements කියවන්න (read only operations) විතරක් ඕනෙ වුණාම.
හිතන්නකෝ, ඔයාට Method එකක් ඕනේ ඕනෑම වර්ගයක List
එකක Elements Print කරන්න:
import java.util.Arrays;
import java.util.List;
public class UnboundedWildcardExample {
public static void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(1, 2, 3);
List<String> strings = Arrays.asList("Hello", "World");
List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
System.out.println("\n--- Printing Integers ---");
printList(integers);
System.out.println("\n--- Printing Strings ---");
printList(strings);
System.out.println("\n--- Printing Doubles ---");
printList(doubles);
}
}
මේ Code
එකේදී printList
Method එක List<?>
එකක් Parameter එකට ගන්නවා. මේ නිසා අපිට List<Integer>
එකක් වුණත්, List<String>
එකක් වුණත්, List<Double>
එකක් වුණත් මේ Method එකට Pass කරන්න පුළුවන්. වැඩේ පහසුයි නේද?
හැබැයි, මේකේ පොඩි Limitations එකකුත් තියෙනවා. List<?>
එකකට අපිට null
හැර වෙන කිසිම Element එකක් Add කරන්න බෑ. මොකද Compile Time එකේදී Type එක ගැන කිසිම දෙයක් දන්නේ නැති නිසා, Java Compiler එක බයයි වැරදි Type එකක් Add වෙයි කියලා. ඒ නිසා, Unbounded Wildcard
එක ගොඩක් වෙලාවට පාවිච්චි කරන්නේ Data කියවන්න විතරයි.
import java.util.ArrayList;
import java.util.List;
public class UnboundedWildcardLimitations {
public static void main(String[] args) {
List<?> unknownList = new ArrayList<Integer>(); // Can be any type
// unknownList.add(new Object()); // Compile-time error! (Cannot add elements other than null)
// unknownList.add("Hello"); // Compile-time error!
unknownList.add(null); // This is allowed
// You can read elements, but they are treated as Object
Object obj = unknownList.isEmpty() ? null : unknownList.get(0);
System.out.println("First element (if any): " + obj);
}
}
Upper-Bounded Wildcards (? extends T) - උඩින් සීමා වෙච්ච ඒවා
ඊළඟට අපි කතා කරමු Upper-Bounded Wildcards
ගැන. මේක අපි ? extends T
කියලා දානවා. මේකෙන් කියවෙන්නේ "T
කියන Type එකට හෝ T
එකෙන් Extend
වෙන ඕනෑම Subtype එකකට" කියන එක. මේක ගොඩක් වෙලාවට පාවිච්චි කරන්නේ Data Collection එකකින් Data කියවන්න (read) විතරක් ඕනෙ වුණාම.
හිතන්නකෝ, ඔයාට Method එකක් ඕනේ Number
වර්ගයේ (Integer
, Double
, Float
වගේ) List
එකක එකතුව හොයන්න:
import java.util.Arrays;
import java.util.List;
public class UpperBoundedWildcardExample {
// This method can sum elements from any List whose type extends Number
public static double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number n : list) { // We can safely read Numbers or their subtypes
sum += n.doubleValue();
}
return sum;
}
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
List<Number> numbers = Arrays.asList(10, 20.5, 30L); // Contains Integer, Double, Long
System.out.println("Sum of integers: " + sumOfList(integers)); // Works! Output: 15.0
System.out.println("Sum of doubles: " + sumOfList(doubles)); // Works! Output: 6.6
System.out.println("Sum of numbers: " + sumOfList(numbers)); // Works! Output: 60.5
// Trying to add elements to a List declared with ? extends Number is NOT allowed (except null).
// List<? extends Number> numList = new ArrayList<Integer>();
// numList.add(new Integer(10)); // Compile-time error!
}
}
මේ Code
එකේදී sumOfList
Method එක List<? extends Number>
එකක් Parameter එකට ගන්නවා. මේ නිසා අපිට List<Integer>
, List<Double>
වගේ Number
එකෙන් extend
වෙන ඕනෑම Type එකක List එකක් මේ Method එකට Pass
කරන්න පුළුවන්. ඒක තමයි මේකේ නියම වාසිය!
හැබැයි, ? extends T
එක්ක අපිට Collection
එකට Element Add කරන්න බෑ (null හැර). ඒකට හේතුව, List<? extends Number>
කියන List
එක ඇත්තටම List<Integer>
එකක්ද, List<Double>
එකක්ද කියලා Compile Time එකේදී හරියටම දන්නේ නැති නිසා. Type Safety එකට හානි වෙන්න පුළුවන් නිසා Java මේක Block
කරනවා.
Lower-Bounded Wildcards (? super T) - යටින් සීමා වෙච්ච ඒවා
අවසාන වශයෙන් අපි කතා කරමු Lower-Bounded Wildcards
ගැන. මේක අපි ? super T
කියලා දානවා. මේකෙන් කියවෙන්නේ "T
කියන Type එකට හෝ T
එකේ Supertype
එකකට" කියන එක. මේක ගොඩක් වෙලාවට පාවිච්චි කරන්නේ Data Collection එකකට Data Add කරන්න (write) ඕනෙ වුණාම.
හිතන්නකෝ, ඔයාට Method එකක් ඕනේ List එකකට Integer
Value Add කරන්න. හැබැයි ඒ List එක Integer
හරි Integer
වල Supertype
එකක් (Number
, Object
) හරි වෙන්න පුළුවන්:
import java.util.ArrayList;
import java.util.List;
public class LowerBoundedWildcardExample {
// This method can add Integers to a List whose type is Integer or a supertype of Integer
public static void addIntegers(List<? super Integer> list) {
list.add(10); // Works! We are guaranteed that Integer is acceptable
list.add(20); // Works!
list.add(Integer.valueOf(30)); // Works!
// list.add(new Double(10.5)); // Compile-time error! Cannot add anything that is not an Integer or its subtype (or null)
}
// Helper method to print any List
public static void printList(List<?> list) {
for (Object o : list) {
System.out.print(o + " ");
}
System.out.println();
}
public static void main(String[] args) {
List<Integer> integers = new ArrayList<>();
addIntegers(integers);
System.out.print("Integers list after adding: ");
printList(integers); // Output: 10 20 30
List<Number> numbers = new ArrayList<>();
addIntegers(numbers);
numbers.add(40.5); // Can add other Numbers if it's a List<Number>
System.out.print("Numbers list after adding: ");
printList(numbers); // Output: 10 20 30 40.5
List<Object> objects = new ArrayList<>();
addIntegers(objects);
objects.add("Hello"); // Can add other Objects if it's a List<Object>
System.out.print("Objects list after adding: ");
printList(objects); // Output: 10 20 30 Hello
}
}
මේ Code
එකේදී addIntegers
Method එක List<? super Integer>
එකක් Parameter එකට ගන්නවා. මේ නිසා අපිට List<Integer>
, List<Number>
, List<Object>
වගේ Integer
වල Supertype
එකක List
එකක් මේ Method එකට Pass
කරන්න පුළුවන්.
? super T
එක්ක අපිට T Type
එකේ හරි T Type
එකේ Subtype
එකක හරි Element Add කරන්න පුළුවන්. ඒත් Read කරන්න පුළුවන් Object
විදියට විතරයි. මොකද List
එකේ තියෙන්න පුළුවන් Integer
වල Supertype
එකක් නිසා, Compiler
එකට දන්නේ නෑ ඒක හරියටම මොන Type
එකක්ද කියලා. ඒ නිසා කියවනකොට Object
විදියට කියවන්න වෙනවා.
PECS (Producer Extends, Consumer Super) Principle
දැන් ඔයාට පැහැදිලියි නේද මේ Wildcards තුන කොහොමද වැඩ කරන්නේ කියලා. මේක මතක තියාගන්න පහසු විදියක් තමයි "PECS" (Producer Extends, Consumer Super) කියන රීතිය.
- Producer Extends: ඔයා Data Collection එකකින් දේවල් කියවනවා නම් (produce කරනවා නම්),
? extends T
පාවිච්චි කරන්න. - Consumer Super: ඔයා Data Collection එකකට දේවල් දානවා නම් (consume කරනවා නම්),
? super T
පාවිච්චි කරන්න. - ඔයා කියවනවාත් නැත්නම්, ලියනවාත් නැත්නම්,
?
(Unbounded) පාවිච්චි කරන්න.
අවසන් වශයෙන්
ඉතින්, Wildcards
ගැන මේ පොඩි පැහැදිලි කිරීමෙන් ඔයාගේ Java Generics දැනුම ටිකක් දියුණු වෙන්න ඇති කියලා හිතනවා. මේවා මුලින් ටිකක් පැටලෙනවා වගේ දැනුනත්, නිතරම Practice කරනකොට ලේසි වෙනවා. ඔයාගේ Code
කරනකොට මේ Wildcards
පාවිච්චි කරලා බලන්න. විශේෂයෙන්ම, Java Collections Framework
එකේ තියෙන Copy
, AddAll
වගේ Methods
වල මේවා ගොඩක් පාවිච්චි කරනවා.
ඔයාට මේ ගැන තව මොනවා හරි ප්රශ්න තියෙනවා නම්, පහලින් Comment Section එකේ දාන්න. අපි ඒ ගැන කතා කරමු. ඒ වගේම, මේ Article එකට කැමති නම්, යාළුවෝ එක්ක Share කරන්නත් අමතක කරන්න එපා. තව මේ වගේ Technical Blog Posts අරගෙන එන්න අපි බලාගෙන ඉන්නවා! එහෙනම්, ආයෙත් දවසක හමුවෙමු! Happy Coding!