Asked  7 Months ago    Answers:  5   Viewed   26 times

Consider this example (typical in OOP books):

I have an Animal class, where each Animal can have many friends.
And subclasses like Dog, Duck, Mouse etc which add specific behavior like bark(), quack() etc.

Here's the Animal class:

public class Animal {
    private Map<String,Animal> friends = new HashMap<>();

    public void addFriend(String name, Animal animal){
        friends.put(name,animal);
    }

    public Animal callFriend(String name){
        return friends.get(name);
    }
}

And here's some code snippet with lots of typecasting:

Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());

((Dog) jerry.callFriend("spike")).bark();
((Duck) jerry.callFriend("quacker")).quack();

Is there any way I can use generics for the return type to get rid of the typecasting, so that I can say

jerry.callFriend("spike").bark();
jerry.callFriend("quacker").quack();

Here's some initial code with return type conveyed to the method as a parameter that's never used.

public<T extends Animal> T callFriend(String name, T unusedTypeObj){
    return (T)friends.get(name);        
}

Is there a way to figure out the return type at runtime without the extra parameter using instanceof? Or at least by passing a class of the type instead of a dummy instance.
I understand generics are for compile time type-checking, but is there a workaround for this?

 Answers

56

You could define callFriend this way:

public <T extends Animal> T callFriend(String name, Class<T> type) {
    return type.cast(friends.get(name));
}

Then call it as such:

jerry.callFriend("spike", Dog.class).bark();
jerry.callFriend("quacker", Duck.class).quack();

This code has the benefit of not generating any compiler warnings. Of course this is really just an updated version of casting from the pre-generic days and doesn't add any additional safety.

Tuesday, June 1, 2021
 
alioygur
answered 7 Months ago
31

You need to make it a generic method, like this:

public static T ConfigSetting<T>(string settingName)
{  
    return /* code to convert the setting to T... */
}

But the caller will have to specify the type they expect. You could then potentially use Convert.ChangeType, assuming that all the relevant types are supported:

public static T ConfigSetting<T>(string settingName)
{  
    object value = ConfigurationManager.AppSettings[settingName];
    return (T) Convert.ChangeType(value, typeof(T));
}

I'm not entirely convinced that all this is a good idea, mind you...

Wednesday, June 2, 2021
 
mnagel
answered 7 Months ago
45

So how can I fix this warning ?

You can use a type parameter for your class :

public class GridModelHolder<T> {
   private List<T>  gridModel;

   public List<T> getGridModel() {
    return gridModel;
   }
}

The client code can then decide what type of List GridModelHolder holds :

GridModelHolder<String> gridModelHolder = new GridModelHolder<String>(new ArrayList<String>);

However, if you insist on using raw types, you can either suppress the warnings or simply have a List of objects (Neither of these are recommended)

@SuppressWarnings("unchecked")
public class GridModelHolder {
   private List  gridModel;

   public List getGridModel() {
    return gridModel;
   }
}

OR

public class GridModelHolder {
   private List<Object>  gridModel;

   public List<Object> getGridModel() {
    return gridModel;
   }
}
Thursday, July 29, 2021
 
pocketfullofcheese
answered 5 Months ago
97

I think you misunderstand how Java generics work:

<T extends Foo> T method() {

This means that the caller of that method can pick whatever subtype of Foo they want and ask for it. For example, you could write

Foo foo = new Foo();
SubFoo subfoo = foo.<SubFoo>method();

...and expect a SubFoo back, but your method implementation couldn't return a SubFoo, and this would have to fail. (I don't speak Scala, but I presume this means that your Scala implementation is not in fact "equivalent.")

If you want your method to be able to return a subtype of Foo that the implementation chooses, instead of the caller, then just write

Foo method() {
  return this;
}
Saturday, August 7, 2021
 
LukeP
answered 4 Months ago
13

This also works, just adding the new() constraint directly to T (not to BaseDropDown)

public static T MapToBaseDropDown3<T>(this GenericDropDownData dd) where T : BaseDropDown, new()
{
    return new T()
    {
        Id = dd.Id,
        Description = dd.Description
    };
}
Sunday, August 15, 2021
 
ProfK
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share