1

I have a function that should take an instance of anything extending Object, and just pass the casted instance to a function. I don't want to use a switch, as the function can accept a huge number of object types, so it would become a very big method.

public void attachBufferData(ContextConstant bufferType, Object<T> data, ContextConstant usage) {
    glBufferData(bufferType.getGLType(), (T) data, usage.getGLType());
}

The code above doesn't work (as Object isn't a generic type), but it should get across what I'm trying to do.

----- EDIT -----

Ok, I tried this:

public void attachBufferData(ContextConstant bufferType, Object data, Class<?> dataType, ContextConstant usage) {
    glBufferData(bufferType.getGLType(), dataType.cast(data), usage.getGLType());
}

But I get a compile error glBufferData(int, long, int) is not applicable for arguments (int, capture#1-of ?, int). I guess it's a massive switch statement then :(

Isaac Woods
  • 1,114
  • 1
  • 17
  • 28

2 Answers2

2

You can't do it like this, I'm afraid. There are three things to consider. I think (2) is the one you really want, but I'm not absolutely certain, so I've left all three issues in there for you to think about.

  1. What signature does glBufferData() have (if it's not overloaded)? If its second parameter is of type Object, then whatever you pass will end up being viewed as an Object there, even if it's a subclass, so you wouldn't achieve anything by having it cast. You might as well just have the type of data as the same type as the second parameter to glBufferData().
  2. If glBufferData() is an overloaded method, and you want to be calling the right one, then you can't do it dynamically: you need some code to test the real type of the class at runtime, and then you choose the right version to call. Choice of overloaded method gets resolved at compile time, not runtime, so you can't just pass it a specific instance you don't know about at compile time and then have it select the right version.
  3. If glBufferData() is a non-overloaded method you've written, contained within your class, then you do have another and better option, which is to make your class generic. If your class takes a type parameter of T, then you can have T data as the second parameter of attachBufferData(), and you can have T data as the second parameter of glBufferData(), so that the types match up.

The point about method overloading is that it's not nearly as clever as it looks. As far as the compiler is concerned, there's really no difference between these two cases.

Case 1:

public void f(int x);
public void f(String s);

Case 2:

public void f(int x);
public void g(String s);

Although we think of case 1 as having just one overloaded method, and case 2 as having two separate methods, as far as the compiler's concerned, in each case there are two distinct methods, and they're distinct because they have distinct signatures (ignoring return type). In both cases, the compiler can choose the right method to call based on the code you write, because it can look at the type of the arguments and the name of the method you've asked for, and find one that matches. The fact that two have the same name is of no more significance than having two methods with different names but the same parameter types.

There's no mechanism for choosing which method to call at runtime in case 1, any more than there is in case 2.

chiastic-security
  • 20,430
  • 4
  • 39
  • 67
  • So you mean that `SomeClass.cast(obj)` wouldn't actually call the correct method or would just result in an error? If we had one method signature with `SomeClass` and one with `SomeOtherClass`? – plalx Oct 03 '14 at 19:15
  • @plalx The compiler can do it only based on the declared type of `obj`. So if you've declared it with `SomeClass obj = new SomeClass()` then it will choose the `SomeClass` version. If you've declared it with `Object obj = new SomeClass()` then it will look for a method signature that takes an `Object` and complain if it can't find one. It won't consider the actual type, because that can't be determined at compile time. – chiastic-security Oct 03 '14 at 19:23
  • What about this? `SomeClass obj = new SomeClass();` then you call a method `someMethod(obj, obj.class)` and within that method `someOtherMethod(passedClass.cast(obj))`? – plalx Oct 03 '14 at 19:26
  • @plalx There's more about this kind of thing in [this question](http://stackoverflow.com/questions/1555326/java-class-cast-vs-cast-operator) that might interest you... – chiastic-security Oct 03 '14 at 19:40
  • The functions are provided by a native library, so Option 3 is no use. glBufferData doesn't take an Object, it has multiple functions (each with different signatures?) taking different types as the second parameter. I guess I will just need a huge switch *sigh*. – Isaac Woods Oct 04 '14 at 08:22
  • 1
    @IsaacWoods yes, that's what I thought, though it was possible you were invoking your own `glBufferData` method, which is why I left option 3 in there. Correct you will need a huge switch and correct you should *sigh* heavily before writing it :) – chiastic-security Oct 04 '14 at 08:25
  • @IsaacWoods btw it occurs that the other option is heavily overloading your `attachBufferData` method instead. Still messy and lots of typing, but maybe slightly cleaner and more in the spirit of things. – chiastic-security Oct 04 '14 at 08:35
  • It should be noted this is a very performance heavy application, would a massive switch or function overloading be faster? – Isaac Woods Oct 04 '14 at 08:37
  • 1
    @IsaacWoods Overloading will be faster because it will all be resolved at compile time. The only question is whether you'll hit essentially the same issue higher up, but if you've constructed your code well, you shouldn't do. – chiastic-security Oct 04 '14 at 08:47
  • Ok, thanks. Will overload it, I don't think I'll end up with the same problem higher up, this is only for abstraction. Thanks again! – Isaac Woods Oct 05 '14 at 09:29
0

You can declare the type at class level and reuse it wherever required, below is an example.

public class ExceptionHolder<T>
{
    private List<T> errors      = new ArrayList<T>();

    public void addError( T t )
    {
        errors.add( t );
    }

    public void addError( List<T> t )
    {
        errors.addAll( t );
    }
}

In the calling code

ExceptionHolder<String> exHolder = new ExceptionHolder<String>();

String can be substitued for any object as needed.

user1339772
  • 783
  • 5
  • 19