473,425 Members | 1,829 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes and contribute your articles to a community of 473,425 developers and data experts.

Advanced Generics: Wildcards

Nepomuk
3,112 Expert 2GB
The basics of Generics are explained in the article Generic Classes in Java by sreekandank. However, there is more to learn than explained in that article. Here I will talk about one specific feature of Java Generics called wildcards.
  1. Basic wildcards
    Generics can be very useful, but sometimes they can be limiting. For example, say you have a generic class Wildcard<T> which has an List<T> object. This so far is no problem.
    Expand|Select|Wrap|Line Numbers
    1. public class Wildcards<T> {
    2.  
    3.     private List<T> list;
    4.     public void setList(List<T> list) {
    5.         this.list = list;
    6.     }
    7. }
    However, say we want to now write a function which will compare the length of the internal list with another List which is of course also generic. This should happen within the Wildcard<T> class and it should not depend on this second List having the same generic Type as the first one.
    A first attempt could be to make the Wildcard class more generic: Wildcard<T1,T2>. Now we can write our function.
    Expand|Select|Wrap|Line Numbers
    1.  public class Wildcards<T1,T2> {
    2.  
    3.     private List<T1> list;
    4.     public void setList(List<T1> list) {
    5.         this.list = list;
    6.     }
    7.     public boolean hasMoreElementsThan(List<T2> otherList) {
    8.         return list.size() > otherList.size();
    9.     }
    10. }
    That should work. Let’s test it:
    Expand|Select|Wrap|Line Numbers
    1. public static void main(String[] args) {
    2.     // Create a new ArrayList of Strings and fill it with "Hello", "World" and "!"
    3.     ArrayList<String> list1 = new ArrayList<String>();
    4.     list1.add("Hello");
    5.     list1.add("World");
    6.     list1.add("!");
    7.     // Create a new Wildcard<String> object and set list1 as the internal list 
    8.     Wildcards<String,Integer> w1 = new Wildcards<String,Integer>();
    9.     w1.setList(list1);
    10.     // Create a new LinkedList of Integers and fill it with the Numbers 0 through 9
    11.     ArrayList<Integer> list2 = new ArrayList<Integer>();
    12.     for(int i=0; i<10; i++) {
    13.         list2.add(new Integer(i));
    14.     }
    15.     // Use the <i>hasMoreElementsThan</i> function
    16.     if(w1.hasMoreElementsThan(list2)) {
    17.         System.out.println("The internal list is bigger than the one it was compared to");
    18.     } else {
    19.         System.out.println("The internal list is not bigger than the one it was compared to");
    20.     }
    21. }
    Great, that should work. But are there any problems? Well, yes. Say we want to also compare the internal list to a third List. We would have to either create a new Wildcard instance for each type we want to compare our list to or make the list of types longer each time we want to add a new comparison. That’s not very efficient. Another idea might be to cast the second List to a more general type, such as LinkedList<Object>. Trying that however will result in the following error message:
    Expand|Select|Wrap|Line Numbers
    1.  Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    2.     Cannot cast from LinkedList<Integer> to LinkedList<Object>
    Why so? Well, if that kind of cast were allowed, one could also do something like this:
    Expand|Select|Wrap|Line Numbers
    1. ((LinkedList<Object>) list2).add(42);
    I hope you see the problem.
    Anyway, this is why there is the concept of wildcards in Java; rather than adding a further generic type for comparison we tell the hasMoreElementsThan function ”I don’t really care, what sort of list it is I’m comparing to.” And here’s how that works:
    Expand|Select|Wrap|Line Numbers
    1. public boolean hasMoreElementsThan(List<?> otherList) {
    2.     return list.size() > otherList.size();
    3. }
    The question mark means that we don’t care about the type. It is not the same as casting it to a List<Object>, because in this case we have no information whatsoever about what kind of Object may be in the List. We don’t have any information about the generic type through the call.

    Note: It is still possible to do stuff like
    Expand|Select|Wrap|Line Numbers
    1. if(otherList.get(0) instanceof String) { … }
  2. Upper-bound wildcards
    Though this can be useful, sometimes we want more control over what kinds of parameters can be handed to a function. Say for example, we want to create a further function in the Wildcards class that will check whether one of the numbers in a List of numbers is close to the size of our internal list. This could look something like this:
    Expand|Select|Wrap|Line Numbers
    1. public boolean closeToSize(List<Number> otherList) {
    2.     for(Number item : otherList) {
    3.         if(item.intValue() == list.size())
    4.             return true;
    5.     }
    6.     return false;
    7. }
    But wait – that only works if our List is actually a List<Number> but not if it’s a List<Integer> or a List<Double>. What to do?
    Luckily, there are methods of limiting wildcards. Upper-bound wildcards are one of the two methods that are offered to do that. Basically, we say ”allow any generic object that extends Number”. And writing this is luckily very easy:
    Expand|Select|Wrap|Line Numbers
    1.  public boolean closeToSize(List<? Extends Number> otherList) {
    2.     // Same as before
    3. }
    Note: The extends X syntax doesn’t just work for wildcards – if you want access to the generic type, you can also have something like
    Expand|Select|Wrap|Line Numbers
    1.  public <S extends Number> boolean closeToSize2(List<S> otherList) {
    2.     for(int i=0; i<otherList.size(); i++) {
    3.         S item = otherList.get(i);
    4.         if(item.intValue() == list.size())
    5.             return true;
    6.     }
    7.     return false;
    8. }
    This should only be used however if the type is actually needed.
  3. Lower-bound wildcards
    If there are upper-bound wildcards then intuition tells us that there are probably lower-bound ones too. And indeed, they exist! The reason they are needed is known as the PECS principle: Producer Expands, Consumer Super. But what does that mean?
    In the upper-bound example, the List produced items with which we could do things. Because of that, we needed some information on what kind of objects the contents may be. If however we want to create a new List and have this new List consume Objects (= add Objects to the List), we need to know what can be put into it. An example:
    Expand|Select|Wrap|Line Numbers
    1.  public void unite(List<? extends Form> listA, List<? extends Form> listB) {
    2.     for(Form item : listA) {
    3.         listB.add(item);
    4.     }
    5. }
    with
    Expand|Select|Wrap|Line Numbers
    1.  abstract class Form {
    2.     protected double area;
    3.  
    4.     public double getArea() {
    5.         return area;
    6.     }
    7. }
    8.  
    9. class Ellipse extends Form {
    10.     public Ellipse(int rx, int ry) {
    11.         super();
    12.         area = Math.PI * rx * ry;
    13.     }
    14. }
    15.  
    16. class Circle extends Ellipse {
    17.     public Circle(int r) {
    18.         super(r,r);
    19.     }
    20. }
    21.  
    22. class Rectangle extends Form {
    23.     public Rectangle(int x, int y) {
    24.         area = x * y;
    25.     }
    26. }
    27.  
    28. class Square extends Rectangle {
    29.     public Square(int x) {
    30.         super(x,x);
    31.     }
    32. }
    If we try to compile this, and call it with w1.unite(list4, list5) (the Lists being of the types ArrayList<Ellipse> and ArrayList<Circle> respectively) the following RuntimeException is thrown:
    Expand|Select|Wrap|Line Numbers
    1.  Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    2.     The method add(capture#4-of ? extends Form) in the type List<capture#4-of ? extends Form> is not applicable for the arguments (Form)
    3.  
    The reason for this is that the program has no way of knowing, whether the two generic types are compatible. After all, from what it knows we may be trying to add Squares to a List of of Circles! For this reason, we can limit wildcards in the other direction too:
    Expand|Select|Wrap|Line Numbers
    1.  public void unite(List<? extends Circle> listA, List<? super Ellipse> listB) {
    2.     for(Circle item : listA) {
    3.         listB.add(item);
    4.     }
    5. }
    This means, that the items of listB can be anything that is in the inheritance chain of Ellipse; this is of course only Form (which is abstract) and Ellipse itself. But, as we’re not reading from listB but only writing to it and Circles can be interpreted as Ellipses, this will work.
So, this has been a detailed explanation of what Wildcards in the world of Java Generics are. The full code can be downloaded below. So now it's time for you to do something with this new knowledge!
Attached Files
File Type: zip Wildcards.java.zip (1.8 KB, 110 views)
May 16 '13 #1
3 4132
It's a nice article Nepomuk.
A very good one! :-)
Dec 9 '13 #2
Nepomuk
3,112 Expert 2GB
Glad you like it :-)
Dec 9 '13 #3
chaarmann
785 Expert 512MB
Very good explanation with even better examples. The examples are concise and point out the problem without having unnecessary code.
Dec 11 '13 #4

Sign in to post your reply or Sign up for a free account.

Similar topics

3
by: Stephen Waits | last post by:
Specifically, I'd like something that goes into detail on expression and meta templates.. Thanks, Steve http://www.gotw.ca/resources/clcm.htm for info about ]
10
by: Alvaro Puente | last post by:
Hi all! Do any of you know if wildcards are accepted when calling rename() function? Thanks/Alvaro
4
by: noname | last post by:
I'm learning C# generics recenly. How do i do to write the similar Java function below in C#? void printCollection(Collection<? extends A> c) { for (Object e: c) { System.out.println(e); }} ...
7
by: Jock | last post by:
Hi all, Could anybody suggest what should be the return type of the method Create in the Factory class? In Java I would have used the wildcards, but here in C# apparently there is no way do do...
9
by: sloan | last post by:
I'm not the sharpest knife in the drawer, but not a dummy either. I'm looking for a good book which goes over Generics in great detail. and to have as a reference book on my shelf. Personal...
19
by: Alan Carpenter | last post by:
Access 8 on Win98 and WinXP I'm having trouble with wildcards in the .Filename property of the FileSearch Object giving different results on win98 and XP. I've been successfully using...
13
by: rkausch | last post by:
Hello everyone, I'm writing because I'm frustrated with the implementation of C#'s generics, and need a workaround. I come from a Java background, and am currently writing a portion of an...
10
by: =?Utf-8?B?S29ucmFkIFJ1ZG9scGg=?= | last post by:
Hello, I was wondering if C# 3.0 finally supported generic upcasting. Consider the following code which does work in C# 2: string xs = {"this", "is", "a", "test"}; object ys = xs; Now,...
5
by: nidaar | last post by:
From a security point of view, is accepting wildcards like "%" in input parameters of stored procedures against any best practices? As an example, if a user defined function uses "Productname...
20
by: -- | last post by:
Imagine I have a class TypeX and a class TypeY that inherts TypeX. public class typeX { .... } public class typeY : typeX { ....
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.