• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1208
  • Last Modified:

Complex nested generic class problem

A complex requirement this:
I have a generic base class PairBase<N extends Number> (N could be one of Short, Byte or Integer). My derived classes will look like this: Data1<Byte>, Data2<Short> etc. In each of my derived class, I will declare "static final" fields of type Pair<N>. I want to ensure that the type used in PairBase and Pair are same (if different, should result in compilation error).

I could make Pair class as a nested (static) class inside PairBase, but I won't be able to use the same "N" type. On the other hand, if I make Pair as non-static nested class, it gives me a warning related to "parameter hiding".

In order to prevent calls to Pair constructor directly, I could provide a static "creator" method inside PairBase. This creator method would also be generic (using the same type 'N' specified in PairBase declaration). However, 'N' is not accessible to static methods inside the generic class!

I have already looked into Google for several solutions including the Sun's "Generics in Java" (Gilad Bracha) and "Java Generics FAQ" (Angelika Langer), but could not figure out a comprehensive solution.

Any help in this regard is appreciated. Here is how I expect the design to be (the code will obviously not compile!)
public class PairBase<N extends Number> {
 
  // Expected static method
  public static Pair<N> createPair(N id, String name) { // Error as N is inaccessible
    return new Pair<N>(id, name);
  }
 
  // Inner class Pair
  public static class Pair<N> { // This 'N' should be same as that in parent class
    private Pair(N id, String name) { // Private constructor
    }
  }
}
 
// Derived class
public class Data1 extends PairBase<Byte> {
  public static final Pair<Byte> C1 = PairBase.create((byte)2, "Value 1");
  public static final Pair<Byte> C2 = PairBase.create((byte)2, "Value 1");
}

Open in new window

0
Raghu Mutalikdesai
Asked:
Raghu Mutalikdesai
  • 5
  • 3
1 Solution
 
Thomas4019Commented:
Yes with Generics in Java, you cannot create any "new" objects involving the genericic type. This is because this genericic type "N" exists really only in the compiler. The program then doesnt know the type when you run the program but assumes the compiler has made sure all the types match.

Is there a specific reason why the setup must be so complicated? I am really comfused why there must a class PairBase<N> and class Pair<N>. I think the confusion in this program is from having both of these interacting.

What is the real goal of this program. Is the main idea that you pair up a Number with a String? So that then by the number you can get the String? If so, that sounds like a Map. Here are some links about Java Maps.

http://www.java-examples.com/iterate-through-values-java-hashmap-example
http://java.sun.com/docs/books/tutorial/collections/interfaces/map.html

0
 
Raghu MutalikdesaiSenior Manager in a leading IT Services companyAuthor Commented:
Well, map won't help my cause. I just require the Data1, Data2 classes that have Pair<N> members. The PairBase<N> came up later in my design process to ensure few things. Firstly, my "Data" classes are plenty in number. These are a kind of hybrid enumeration types that I use in my user interface layer (for display purpose) as well as in backend (for numeric comparison). Secondly, PairBase<N> surfaced because I am using reflection to determine Data classes based on input "N" value (that come from database).

Not to add further confusion, I require the generic Data classes containing Pair<N>. However, the Data classes will have multiple Pair<N> members. I want to enforce this constraint on my team members to use same "N" in all PAir<N> members of "Data" class.

I hope this made sense!
0
 
Kevin CrossChief Technology OfficerCommented:
Try like this:
public class PairBase<N extends Number> {
 
  // Expected static method
  public static Pair<?> createPair(Number id, String name) { // Error as N is inaccessible
    return new Pair(id, name);
  }
 
  // Inner class Pair
  public static class Pair<N> { // This 'N' should be same as that in parent class
    private Pair(N id, String name) { // Private constructor
    }
  }
}

Open in new window

0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
Raghu MutalikdesaiSenior Manager in a leading IT Services companyAuthor Commented:
Wild card is absolutely No No here! If I use Pair<?>, then my derived class declarations will also have to use Pair<?>. Besides the basic purpose is getting defeated. Here is an example of how I want the enforcement to happen:
public class D1 extends PairBase<Short> {
  public static final Pair<Short> V1 = PairBase.create((short)4, "m0");
  public static final Pair<Short> V2 = PairBase.create((short)7, "iK");
  public static final Pair<Byte> V3 = PairBase.create((byte)8, "qS"); // This should result in compilation error because D1 extends from PairBase with type as "Short". I want all members of D1 to use "Short". Therefore, using '?' or 'Number' wouldn't work for me.
}

Open in new window

0
 
Kevin CrossChief Technology OfficerCommented:
Firstly, you can do this in the derived class to get pass the wildcard:

 public static final PairBase.Pair<Byte> C1 = (Pair<Byte>) PairBase.createPair((byte)2, "Value 1");
  public static final PairBase.Pair<Byte> C2 = (Pair<Byte>) PairBase.createPair((byte)2, "Value 1");

But based on what you just said, I don't think you want createPair to be public static.  If you want createPair to behave a specific way based on how PairBase is extended then just make it a normal method within pairBase that will get used when inherited.  I will post what I mean.
0
 
Kevin CrossChief Technology OfficerCommented:
Here is an example.  Not most optimal yet as you have to instantiate the derived class since you want public static final variables and we have made createPair non-static, but maybe will help you find the path you need...
/** PairBase.java */
public class PairBase<N extends Number> {
 
  // Expected static method
  Pair<N> createPair(N id, String name) { // Error as N is inaccessible
    return new Pair(id, name);
  }
 
  // Inner class Pair
  public static class Pair<N> { // This 'N' should be same as that in parent class
    private Pair(N id, String name) { // Private constructor
 
    }
  }
}
 
 
/** Data1.java */
// This won't compile because PairBase derived as Short
public class Data1 extends PairBase<Short> {
  public static final PairBase.Pair<Byte> C1;
  public static final PairBase.Pair<Byte> C2;
  
  static {
      Data1 d = new Data1();
      C1 = d.createPair((byte)2, "Value 1");
      C2 = d.createPair((byte)2, "Value 1");
  }
}
 
 
/** Data1.java version 2 */
// This compiles
public class Data1 extends PairBase<Byte> {
  public static final PairBase.Pair<Byte> C1;
  public static final PairBase.Pair<Byte> C2;
  
  static {
      Data1 d = new Data1();
      C1 = d.createPair((byte)2, "Value 1");
      C2 = d.createPair((byte)2, "Value 1");
  }
}

Open in new window

0
 
Kevin CrossChief Technology OfficerCommented:
Note this works (causes the compile error you want) also:

public class Data1 extends PairBase<Short> {
  public final PairBase.Pair<Byte> C1 = createPair((byte)2, "Value 1");
  public final PairBase.Pair<Byte> C2 = createPair((byte)2, "Value 1");
}
0
 
Kevin CrossChief Technology OfficerCommented:
Or do something like this:

public class Data1 {
  private static final PairBase<Short> pb = new PairBase<Short>();
  public static final Pair<Byte> C1 = pb.createPair((byte)2, "Value 1");
  public static final Pair<Byte> C2 = pb.createPair((byte)2, "Value 1");
}
0
 
Raghu MutalikdesaiSenior Manager in a leading IT Services companyAuthor Commented:
Perfect!
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

  • 5
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now