4

I want to create a method where 2 or more Object parameters have to be passed, that have to be from the same class.
Object foo and bar have to be members of the same class.

public void method(Object foo, Object bar) {
}

I remember that I have seen something like this before, but I cant remember how it was done exactly.

public void method(Object<?> foo, Object<?> bar) {
}
Busti
  • 5,492
  • 2
  • 21
  • 34
  • 1
    Nice question. This is, by the way, trivial in C++ (`template void method(const Y& foo, const Y& bar)`) Not so easy in Java. You can use generics, but you need to hack the argument list or rely on run-time checks. – Bathsheba Apr 13 '16 at 10:25
  • What is the X-Y problem you are actually trying to solve with this? – Ferrybig Apr 13 '16 at 10:28
  • @Ferrybig I am reading a Json file with settings into an object and Wrote a method to read and write just about any object to json. The first object is empty and is being filled with the settings read from the file, while the second object contains the default values that apply when the settings file is created for the first time or a setting is not found / invalid. – Busti Apr 13 '16 at 10:31
  • @Busti So you actually need the first non-null object for that? In that case [this answer](http://stackoverflow.com/a/2768076/1542723) may help you more for that problem – Ferrybig Apr 13 '16 at 10:34
  • @Ferrybig It dosn't have to be that complicated. I am only using 2 objects, where the second one does always contain the defaults. – Busti Apr 13 '16 at 10:36

4 Answers4

3

I think you mean something like this:

public <T> void method(T foo, T bar) {
}

Here you define the generic type T without any bounds and require the parameters to both be of type T (or a subclass). Then you can call it like this:

method("string1", "string2"); //ok
method(Integer.valueOf(1), Long.valueOf(1) ); //works, Compiler will infer T = Number
this.<Integer>method(Integer.valueOf(1), Long.valueOf(1) ); //You set T = Integer, so the compiler will complain
Thomas
  • 87,414
  • 12
  • 119
  • 157
2

You can do this by checking the class of the object, to see if they are the same

public void method(Object foo, Object bar) {
    if(!foo.getClass().equals(bar.getClass())) {
        throw new IllegalArgumentException("Exception");
    }
}

You can't do this with generics, because java will automatically cast the objects to the Object class for you.

Alternative solution using a third class argument:

public <T> void method(T foo, T bar, Class<T> clazz) {
}

This can be called as:

method("string 1", "string 2", String.class);
Ferrybig
  • 18,194
  • 6
  • 57
  • 79
  • This is basically how I am doing it at the moment, I will have a look into your second solution though. – Busti Apr 13 '16 at 10:26
  • @user3493289 So the code that runs it can actually access the bound that was used, and do decisions based on that, like throwing if its Object – Ferrybig Apr 13 '16 at 10:31
  • You don't need the Class in this case. A simple getClass() check can be used to determine if the types don't match. This would ease the burden for the callers and also not compromise on type safety at compile time by flagging out errors like method("hello" , 1) – MS Srikkanth Apr 13 '16 at 10:34
  • @user3493289 the alternative is meant to not use `getClass()` since that would move the check to runtime and not compile time. – Thomas Apr 13 '16 at 10:37
  • @Thomas, can you tell me in what instances would public void method(T foo, T bar) successfully compile but public void (T foo, T bar, Class clazz) would fail to compile? – MS Srikkanth Apr 13 '16 at 10:39
  • 1
    @user3493289 try `method("string", Integer.valueOf(1))` - since there's no bound on `T` the compiler is able to infer the common super type `Object`. But if you pass `String.class` as a third parameter the compiler knows that `T` is meant to be `String` and hence the second parameter fails (although you're right, the parameter itself is not necessary since there are other ways to define `T = String`). – Thomas Apr 13 '16 at 10:41
2

try

public <T,U extends T> void method(T foo, U bar) { 
}
Telcontar
  • 4,794
  • 7
  • 31
  • 39
  • Does this also work if foo and bar are both the same class, a string for example? – Busti Apr 13 '16 at 10:34
  • Additional information: if you want to make a call that like `method( Integer.valueOf(1), Long.valueOf(1));` work, i.e. you want `T = Number`, you'd have at least two options here: 1) cast the first value to `Number` or call it like `this.method( ... )`. – Thomas Apr 13 '16 at 10:34
  • @Busti `U extends T` means that `U` is _at least_ the type of `T`, hence if you call it with two strings it will be `U = T = String` and all is fine. – Thomas Apr 13 '16 at 10:35
0

An alternative solution with Java 8 would be to use pattern like this:

public <T> Consumer<T> method(T foo) {
    return bar -> {
        // do stuff;
    };
}

@Test
public void test() {
    method(Integer.valueOf(1)).accept(Long.valueOf(2)); // No good
    method(Integer.valueOf(1)).accept(Integer.valueOf(1)); // ok
    method((Number) Integer.valueOf(1)).accept(Long.valueOf(2)); // ok
}
Teemu Ilmonen
  • 316
  • 1
  • 5