考慮如下示例(OOP書籍中的典型示例): java
我有一個Animal
課,每一個Animal
能夠有不少朋友。
還有子類,如Dog
, Duck
, Mouse
等,它們添加了特定的行爲,如bark()
, quack()
等。 ide
這是Animal
類: this
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); } }
這是一些帶有大量類型轉換的代碼片斷: spa
Mouse jerry = new Mouse(); jerry.addFriend("spike", new Dog()); jerry.addFriend("quacker", new Duck()); ((Dog) jerry.callFriend("spike")).bark(); ((Duck) jerry.callFriend("quacker")).quack();
有什麼辦法能夠將泛型用於返回類型來擺脫類型轉換,因此我能夠說 code
jerry.callFriend("spike").bark(); jerry.callFriend("quacker").quack();
這是一些帶有返回類型的初始代碼,這些代碼做爲從未使用過的參數傳遞給該方法。 繼承
public<T extends Animal> T callFriend(String name, T unusedTypeObj){ return (T)friends.get(name); }
有沒有一種方法可使用instanceof
在運行時肯定返回類型而無需額外的參數? 或者至少經過傳遞類型的類而不是虛擬實例。
我瞭解泛型用於編譯時類型檢查,可是是否有解決方法? 接口
我知道這是一個徹底不一樣的問題。 解決此問題的另外一種方法是反思。 個人意思是,這沒有從「泛型」中受益,而是讓您以某種方式模仿了您想要執行的行爲(使狗吠,使鴨嘎嘎等等),而無需考慮類型轉換: get
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"); } }
您在這裏尋找的是抽象。 針對接口編寫更多的代碼,您應該減小進行強制轉換。 string
下面的示例在C#中,可是概念保持不變。 it
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!"); } } }
關於什麼
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); }
}
「有沒有一種方法可使用instanceof在運行時肯定返回類型而無需額外的參數?」
做爲一種替代解決方案,您能夠利用此類訪問者模式 。 使Animal抽象並使其實現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); } }
可訪問只是意味着Animal實現願意接受訪問者:
public interface Visitable { void accept(Visitor v); }
訪問者實現能夠訪問動物的全部子類:
public interface Visitor { void visit(Dog d); void visit(Duck d); void visit(Mouse m); }
所以,例如,Dog實現將以下所示:
public class Dog extends Animal { public void bark() {} @Override public void accept(Visitor v) { v.visit(this); } }
這裏的技巧是,因爲Dog知道它是什麼類型,所以能夠經過傳遞「 this」做爲參數來觸發訪問者v的相關重載訪問方法。 其餘子類將以徹底相同的方式實現accept()。
而後,想要調用子類特定方法的類必須實現Visitor接口,以下所示:
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(); } }
我知道它的接口和方法要比您討價還價的要多得多,但這是一種使用零個instanceof檢查和零個類型強制轉換來獲取每一個特定子類型的句柄的標準方法。 並且全部操做均以與標準語言無關的方式完成,所以不只適用於Java,並且任何OO語言都應相同。
我在lib kontraktor中執行了如下操做:
public class Actor<SELF extends Actor> { public SELF self() { return (SELF)_self; } }
子類化:
public class MyHttpAppSession extends Actor<MyHttpAppSession> { ... }
至少這在當前類內部以及具備強類型引用的狀況下有效。 多重繼承是可行的,但隨後變得很是棘手:)