How do I make method return types generic?

Keywords: Java

Consider the following example (typical in OOP books):

I have an Animal lesson, and each Animal can have many friends.
There are also subclasses, such as Dog, Duck, Mouse, and so on, that add specific behaviors, such as bark(), quack().

This is 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);
    }
}

These are snippets of code with a lot of type conversions:

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

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

What can I do to get rid of type conversions by using generics for return types, so I can say

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

This is some initial code with a return type that is passed to the method as a parameter that has never been used.

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

Is there a way to use instanceof to determine the return type at runtime without additional parameters?Or at least by passing a class of type instead of a virtual instance.
I understand that generics are used for compile-time type checking, but is there a workaround?

#1st floor

I know this is a completely different question.Another way to solve this problem is to rethink.I mean, instead of benefiting from generics, it allows you to somehow mimic the behavior you want to perform (barking dogs, rattling ducks, etc.) without considering type conversion:

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

abstract class AnimalExample {
    private Map<String,Class<?>> friends = new HashMap<String,Class<?>>();
    private Map<String,Object> theFriends = new HashMap<String,Object>();

    public void addFriend(String name, Object friend){
        friends.put(name,friend.getClass());
        theFriends.put(name, friend);
    }

    public void makeMyFriendSpeak(String name){
        try {
            friends.get(name).getMethod("speak").invoke(theFriends.get(name));
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    } 

    public abstract void speak ();
};

class Dog extends Animal {
    public void speak () {
        System.out.println("woof!");
    }
}

class Duck extends Animal {
    public void speak () {
        System.out.println("quack!");
    }
}

class Cat extends Animal {
    public void speak () {
        System.out.println("miauu!");
    }
}

public class AnimalExample {

    public static void main (String [] args) {

        Cat felix = new Cat ();
        felix.addFriend("Spike", new Dog());
        felix.addFriend("Donald", new Duck());
        felix.makeMyFriendSpeak("Spike");
        felix.makeMyFriendSpeak("Donald");

    }

}

#2nd floor

What you are looking for here is abstraction.Write more code for the interface, and you should reduce the number of forced conversions.

The following example is in C#, but the concept remains the same.

using System;
using System.Collections.Generic;
using System.Reflection;

namespace GenericsTest
{
class MainClass
{
    public static void Main (string[] args)
    {
        _HasFriends jerry = new Mouse();
        jerry.AddFriend("spike", new Dog());
        jerry.AddFriend("quacker", new Duck());

        jerry.CallFriend<_Animal>("spike").Speak();
        jerry.CallFriend<_Animal>("quacker").Speak();
    }
}

interface _HasFriends
{
    void AddFriend(string name, _Animal animal);

    T CallFriend<T>(string name) where T : _Animal;
}

interface _Animal
{
    void Speak();
}

abstract class AnimalBase : _Animal, _HasFriends
{
    private Dictionary<string, _Animal> friends = new Dictionary<string, _Animal>();


    public abstract void Speak();

    public void AddFriend(string name, _Animal animal)
    {
        friends.Add(name, animal);
    }   

    public T CallFriend<T>(string name) where T : _Animal
    {
        return (T) friends[name];
    }
}

class Mouse : AnimalBase
{
    public override void Speak() { Squeek(); }

    private void Squeek()
    {
        Console.WriteLine ("Squeek! Squeek!");
    }
}

class Dog : AnimalBase
{
    public override void Speak() { Bark(); }

    private void Bark()
    {
        Console.WriteLine ("Woof!");
    }
}

class Duck : AnimalBase
{
    public override void Speak() { Quack(); }

    private void Quack()
    {
        Console.WriteLine ("Quack! Quack!");
    }
}
}

#3rd floor

About what

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

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

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

}

#4th floor

"Is there a way to use instanceof to determine the return type at runtime without additional parameters?"

As an alternative solution, you can take advantage of this Visitor .Make Animal abstract and implement Visitable:

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

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

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

Accessibility simply means that Animal is willing to accept visitors:

public interface Visitable {
    void accept(Visitor v);
}

Visitors implement all subclasses that have access to animals:

public interface Visitor {
    void visit(Dog d);
    void visit(Duck d);
    void visit(Mouse m);
}

Therefore, for example, the Dog implementation would look like this:

public class Dog extends Animal {
    public void bark() {}

    @Override
    public void accept(Visitor v) { v.visit(this); }
}

The trick here is that since Dog knows what type it is, it can trigger the associated overloaded access method of visitor v by passing "this" as a parameter.Other subclasses implement accept () in exactly the same way.

Then, classes that want to call subclass-specific methods must implement the Visitor interface, as follows:

public class Example implements Visitor {

    public void main() {
        Mouse jerry = new Mouse();
        jerry.addFriend("spike", new Dog());
        jerry.addFriend("quacker", new Duck());

        // Used to be: ((Dog) jerry.callFriend("spike")).bark();
        jerry.callFriend("spike").accept(this);

        // Used to be: ((Duck) jerry.callFriend("quacker")).quack();
        jerry.callFriend("quacker").accept(this);
    }

    // This would fire on callFriend("spike").accept(this)
    @Override
    public void visit(Dog d) { d.bark(); }

    // This would fire on callFriend("quacker").accept(this)
    @Override
    public void visit(Duck d) { d.quack(); }

    @Override
    public void visit(Mouse m) { m.squeak(); }
}

I know it has much more interfaces and methods than you bargain for, but it's a standard way to use zero instanceof checks and zero type casts to get handles to each particular subtype.And all operations are done in a way that is independent of the standard language, so it should not only be used in Java, but also in any OO language.

#5th floor

I did the following in lib kontraktor:

public class Actor<SELF extends Actor> {
    public SELF self() { return (SELF)_self; }
}

Subcategorization:

public class MyHttpAppSession extends Actor<MyHttpAppSession> {
   ...
}

This is valid at least within the current class and with strongly typed references.Multiple inheritance is possible, but then becomes very tricky:)

Posted by lJesterl on Thu, 09 Jan 2020 00:08:44 -0800