在一般的開(kāi)發(fā)中,Java的單繼承限制一般不會(huì)引起什么問(wèn)題。實(shí)際上,需要使用多重繼承往往意味著糟糕的設(shè)計(jì)。然而還是存在一些情況,程序員們希望能夠繼承多個(gè)類。雖然Java不允許繼承多個(gè)類,但是有些技巧能夠模擬多重繼承。
我在Swing應(yīng)用程序和Web應(yīng)用中都使用過(guò)這個(gè)技巧。Swing應(yīng)用打包并部署服務(wù)到應(yīng)用服務(wù)器上。這種情況下,因?yàn)槲蚁M诓煌M件間拖放對(duì)象而這些GUI組件共享同一個(gè)拖放方法,這樣的話,所有GUI組件需要擴(kuò)展兩個(gè)類:GUI組件本身(JTree或JList)和通用的拖放類(drag-and-drop class)。本文中介紹的技術(shù)簡(jiǎn)化了拖放類的實(shí)現(xiàn)。
為了更好的闡述Java中多重繼承的技術(shù),讓我們先來(lái)如何在一個(gè)Web應(yīng)用中使用它。在這個(gè)Web應(yīng)用中,Servlet類和其他類需要被擴(kuò)展。我的這個(gè)應(yīng)用很簡(jiǎn)單,是一個(gè)基于文本的信息傳遞系統(tǒng),能夠通過(guò)移動(dòng)電話、PDA或者其他網(wǎng)絡(luò)終端設(shè)備傳遞信息給另一個(gè)移動(dòng)電話。

如上圖所示,這個(gè)系統(tǒng)的核心是一個(gè)能夠從客戶端接受信息然后傳遞給移動(dòng)電話的服務(wù)。為了簡(jiǎn)化客戶端開(kāi)發(fā),我寫了一個(gè)MessageClient類來(lái)包含所有用于與服務(wù)通信的通用方法。因?yàn)檫@個(gè)類可以最為基類被所有可能的終端設(shè)備使用,因此能夠簡(jiǎn)化客戶端開(kāi)發(fā)。
MessageClient類的源代碼如下:






































































?
這個(gè)類包含3個(gè)方法:
sendMessage()方法將實(shí)際信息發(fā)送到服務(wù)器;
connectServer()方法負(fù)責(zé)連接服務(wù)。在本例中,服務(wù)是一個(gè)RMI服務(wù);
getServerName()是一個(gè)抽象類,因?yàn)椴煌O(shè)備決定服務(wù)名字的方式不同。也就是說(shuō),任何繼承自MessageClient的類都必須實(shí)現(xiàn)getServerName()方法。
在開(kāi)發(fā)與信息服務(wù)通信的客戶端程序時(shí),我們需要多重繼承。我們的網(wǎng)絡(luò)客戶端是一個(gè)簡(jiǎn)單的用來(lái)接收來(lái)自表單的信息并將其傳送到服務(wù)器的Servlet。為了完成上述任務(wù),這個(gè)Servlet必須既繼承HttpServlet又繼承MessageClient。由于Java不允許這種繼承行為,我讓主類繼承HttpServlet,讓主類中的內(nèi)部類繼承MessageClient,然后外部類創(chuàng)建了內(nèi)部類的一個(gè)實(shí)例。代碼如下:

public?class?SendMessageServlet?extends?HttpServlet
{

???private?MessageClient?m_messageClient;
???private?String?m_serverName;

???public?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)

?????????throws?ServletException,?IOException
{

??????try
{
????????//Get?server?name
?????????m_serverName?=?request.getServerName();
?????????System.out.println("ServerName?is?"?+?m_serverName);
?????????//Create?message?client?to?communicate?with?message?server
?????????m_messageClient?=?new?ServletMessageClient();
?????????System.out.println("Created?Message?Client");
?????????m_messageClient.connectToServer();

?????????//Get?message?and?phone?number
?????????String?phoneNum?=?(String)?request.getParameter("PhoneNum");
?????????String?message?=?(String)?request.getParameter("Message");

?????????//Send?message
?????????m_messageClient.sendMessage(phoneNum,message);

?????????//Display?page?to?tell?user?message?was?sent
?????????response.setContentType("text/html");
?????????RequestDispatcher?dispatcher?=?getServletContext().getRequestDispatcher("/SendMessageForm.jsp");
?????????dispatcher.include(request,?response);

??????}catch?(Exception?e)
{
?????????e.printStackTrace();
??????}
???}?

???public?void?doPost(HttpServletRequest?request,?HttpServletResponse?response)

?????????throws?ServletException,?IOException???
{
??????doGet(request,?response);
???}


????/**?*//**?Inner?class?used?to?extend?MessageClient?????*/

????public?class?ServletMessageClient?extends?MessageClient?
{

???????public?ServletMessageClient()
{
?????????super();
???????}

???????public?String?getServerName()
{
??????????System.out.println("Returning?ServerName?"?+?m_serverName);
??????????return(m_serverName);
???????}
????}
}?
這種方法并不是真正的多重繼承,因?yàn)槲覀兪褂昧舜?例如:MessageClient被很多外部類繼承,但并不是外部類本身),但是效果是一樣的。雖然MessageClient能被單獨(dú)的類擴(kuò)展,但是使用內(nèi)部類允許它訪問(wèn)外部類中的所有成員和方法。這讓兩個(gè)類之間的交互變得很容易。這個(gè)例子只擴(kuò)展了兩個(gè)類,你當(dāng)然可以使用這種技術(shù)擴(kuò)展任意多類。
當(dāng)然,其實(shí)這個(gè)信息傳遞服務(wù)的例子不用多重繼承也能實(shí)現(xiàn)。如果MessageClient有一個(gè)能夠接收服務(wù)服務(wù)名字的構(gòu)造函數(shù),那么getServerName()方法就可以不是抽象的。這意味著Web客戶端可以不必繼承MessageClient類。
最后提醒開(kāi)發(fā)者,只有在有明確的理由,確定要使用上面技術(shù)時(shí)才使用,并且使用時(shí)要小心。因?yàn)槎嘀乩^承使設(shè)計(jì)變得復(fù)雜,并且很容易被誤用。