DZone
Java Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Java Zone > Overload API methods with care – the sequel

Overload API methods with care – the sequel

Lukas Eder user avatar by
Lukas Eder
·
Nov. 22, 11 · Java Zone · Interview
Like (0)
Save
Tweet
3.33K Views

Join the DZone community and get the full member experience.

Join For Free

I had recently blogged about funny issues that arise when overloading API methods with generics involved:
http://lukaseder.wordpress.com/2011/11/11/overload-api-methods-with-care/

I promised a sequel as I have encountered more trouble than that, so here it is.

The trouble with generics and varargs

Varargs are another great feature introduced in Java 5. While being merely syntactic sugar, you can save quite some lines of code when passing arrays to methods:

// Method declarations with or without varargs
public static String concat1(int[] values);
public static String concat2(int... values);

// The above methods are actually the same.
String s1 = concat1(new int[] { 1, 2, 3 });
String s2 = concat2(new int[] { 1, 2, 3 });

// Only, concat2 can also be called like this, conveniently
String s3 = concat2(1, 2, 3);

That’s well-known. It works the same way with primitive-type arrays as with Object[]. It also works with T[] where T is a generic type!

// You can now have a generic type in your varargs parameter:
public static <T> T[] array(T... values);

// The above can be called "type-safely" (with auto-boxing):
Integer[] ints   = array(1, 2, 3);
String[] strings = array("1", "2", "3");

// Since Object could also be inferred for T, you can even do this:
Object[] applesAndOranges = array(1, "2", 3.0);

The last example is actually already hinting at the problem. If T does not have any upper bound, the type-safety is gone, completely. It is an illusion, because in the end, the varargs parameter can always be inferred to “Object…”. And here’s how this causes trouble when you overload such an API.

// Overloaded for "convenience". Let's ignore the compiler warning
// caused when calling the second method
public static <T> Field<T> myFunction(T... params);
public static <T> Field<T> myFunction(Field<T>... params);

At first, this may look like a good idea. The argument list can either be constant values (T…) or dynamic fields (Field…). So in principle, you can do things like this:

// The outer function can infer Integer for <T> from the inner
// functions, which can infer Integer for <T> from T...
Field<Integer> f1 = myFunction(myFunction(1), myFunction(2, 3));

// But beware, this will compile too!
Field<?> f2 = myFunction(myFunction(1), myFunction(2.0, 3.0));

The inner functions will infer Integer and Double for <T>. With incompatible return types Field<Integer> and Field<Double>, the “intended” method with the “Field<T>…” argument does not apply anymore. Hence method one with “T…” is linked by the compiler as the only applicable method. But you’re not going to guess the (possibly) inferred bound for <T>. These are possible inferred types:

// This one, you can always do:
Field<?> f2 = myFunction(myFunction(1), myFunction(2.0, 3.0));

// But these ones show what you're actually about to do
Field<? extends Field<?>>                       f3 = // ...
Field<? extends Field<? extends Number>>        f4 = // ...
Field<? extends Field<? extends Comparable<?>>> f5 = // ...
Field<? extends Field<? extends Serializable>>  f6 = // ...

The compiler can infer something like Field<? extends Number & Comparable<?> & Serializable> as a valid upper bound for <T>. There is no valid exact bound for <T>, however. Hence the necessary <? extends [upper bound]>.

Conclusion

Be careful when combining varargs parameters with generics, especially in overloaded methods. If the user correctly binds the generic type parameter to what you intended, everything works fine. But if there is a single typo (e.g. confusing an Integer with a Double), then your API’s user is doomed. And they will not easily find their mistake, as no one sane can read compiler error messages like this:

Test.java:58: incompatible types
found   : Test.Field<Test.Field<
          ? extends java.lang.Number&java.lang.Comparable<
          ? extends java.lang.Number&java.lang.Comparable<?>>>>
required: Test.Field<java.lang.Integer>
        Field<Integer> f2 = myFunction(myFunction(1),
                                       myFunction(2.0, 3.0));

 

From http://lukaseder.wordpress.com/2011/11/19/overload-api-methods-with-care-the-sequel/

API

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Usage of Java Streams and Lambdas in Selenium WebDriver
  • Troubleshooting Memory Leaks With Heap Profilers
  • How to Configure Git in Eclipse IDE
  • Instancio: Random Test Data Generator for Java (Part 1)

Comments

Java Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo