1

I have a class with a type

public class Scan<T extends Data>{
  ...
}

Data is a abstract type, from which I have some Implementations.

Now I want some kind of chooser, which implementation to use. Is this possible? If yes, which type must the variable have (I tried with Class, but this does not work)

Class datatype;
switch(datatypeInt){
  case 2:
    datatype = Simple3DData.class;
    break;
  case 1:
  default:
    datatype = Simple2DData.class;
}

Scan<datatype> scan = new Scan<>();

(Obvious this does not work)

I can't instantiate Scan in the switch block, because I will also choose the Scan class at some point dynamically.

EDIT: I see, this is not possible that easy, I will try convert the code, not to use types, rather replacing all my T by Data and passing the Class object as parameter for my Scan.

5
  • 1
    Please bear in mind that type parameters are mainly a compile-time tool, What would you do with your Scan<datatype> if you don't know what datatype is at compile time? That is, how would the rest of your code go? Commented Oct 17, 2016 at 15:28
  • There is more code, but I know its of subtype of Data, which has the some abstract functions, implemented in all Subtypes, which I want to use in the Scan class. Commented Oct 17, 2016 at 15:30
  • So can you just declare it as Scan<? extends Data>? Commented Oct 17, 2016 at 15:30
  • Check out the Factory pattern as an alternate, easier way to create different objects based on different inputs. Commented Oct 17, 2016 at 15:31
  • what are Simple3DData and Simple2DData? Commented Oct 17, 2016 at 15:32

3 Answers 3

1

What you try to achieve cannot work because type parameters are only used at compile time by the compiler to make sure that your code is compliant with the types defined in order to avoid getting at runtime exceptions of type ClassCastException.

At runtime, type parameters don't even exist anymore due to type erasure, such that your code would be something like this:

Class datatype;
switch(datatypeInt){
  case 2:
    datatype = Simple3DData.class;
    break;
  case 1:
  default:
    datatype = Simple2DData.class;
}
Scan = new Scan();

Which means that you need to specify the class explicitly, so your code could be something like this:

Class<? extends Data> datatype;
switch(datatypeInt){
  case 2:
    datatype = Simple3DData.class;
    break;
  case 1:
  default:
    datatype = Simple2DData.class;
}
Scan scan = new Scan(datatype);

A much more OO approach could be to implement the strategy pattern, you could have one scanning strategy per type of data, the code would then be:

ScanStrategy strategy;
switch(datatypeInt){
  case 2:
    strategy = new Simple3DDataScanner();
    break;
  case 1:
  default:
    strategy = new Simple2DDataScanner();
}
Scan scan = new Scan(strategy);
Sign up to request clarification or add additional context in comments.

1 Comment

I will drop certainly also the type part. Actually I thought, the the erasure only will happen on the class itself, so that, afterwards you can smash in anything. (Which means the compiler would be happy to accept something derviated from Data.
1

You cannot programmatically pick your parametrized type from a Class<? extends Data> object (or in your case, from a raw Class object).

You could use instanceof on an object of Data reference to check its concrete type, and initialize your Scan accordingly.

But that would probably defile the concept of using a bounded type parameter in the first place, as you want your Scan instance to work with all type parameters assignable from Data.

So, depending on the usage in your Scan class, you may either simply use a Scan<Data>, or a Scan<? extends Data> (upper bound with wildcard), or Scan<? super Data> (lower bound with wildcard) reference.

Here's an example:

class Scan<T extends Data> {
    // this method takes an instance of the Scan's type parameter
    void doSomething(T t){}
    // this method returns some object assignable from Data given some logic
    T returnSomething() {
         // just for test - note that you can't instantiate a generic type
         return null;
    }
}
abstract class Data {}
class Simple3DData extends Data {}
class Simple2DData extends Data {}
// somewhere in some method/initializer...
{
    Scan<Data> scan = new Scan<>();
    // works with both, as both are Data
    scan.doSomething(new Simple3DData());
    scan.doSomething(new Simple2DData());
    // generalized to Data, instance type depends on logic in Scan class
    Data someData = scan.returnSomething();
}

2 Comments

And I would question the need for a type parameter at all if the only valid value is Scan<Data>.
@biziclop wouldn't that allow to simply generalize generic operations on all types of Data?
0

When you are knowing what you do, you can always do something like:

public class Scan<T extends Data>{
  public T createSomeT() {
    Class<T> classToUse = foo();
    return (T) classToUse.newInstance();
}

Meaning: you can use reflection to instantiate objects, and a hard cast to "force" the correct type upon them (and of course, you need to deal with the variety of exceptions that can happen here).

What foo() does on the other hand to return the one Class object you are interested is of course not that easy. One solution would be to pass a real Class object to the constructor of Scan; and then foo() would just be that object passed in.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.