摘要:虽然Java不允许多重继承,但是有些情况却允许其使用。本文将阐述在一个Web应用中模拟多重继承的技术。在一般的开发中,Java的单继承限制一般不会引起什么问题。实际上,需要使用多重继承往往意味着糟糕的设计。然而还是存在一些情况,程序员们希望能够继承多个类。虽然Java不允许继承多个类,但是有些技巧能够模拟多重继承。我在Swing应用程序和Web应用中都使用过这个技巧。Swing应用打包并部署服务到应用服务器上。这种情况下,因为我希望在不同组件间拖放对象而这些GUI组件共享同一个拖放方法,这样的话,所有GUI组件需要扩展两个类:GUI组件本身(JTree或JList)和通用的拖放类(drag-and-drop class)。本文中介绍的技术简化了拖放类的实现。为了更好的阐述Java中多重继承的技术,让我们先来如何在一个Web应用中使用它。在这个Web应用中,Servlet类和其他类需要被扩展。我的这个应用很简单,是一个基于文本的信息传递系统,能够通过移动电话、PDA或者其他网络终端设备传递信息给另一个移动电话。如上图所示,这个系统的核心是一个能够从客户端接受信息然后传递给移动电话的服务。为了简化客户端开发,我写了一个MessageClient类来包含所有用于与服务通信的通用方法。因为这个类可以最为基类被所有可能的终端设备使用,因此能够简化客户端开发。MessageClient类的源代码如下:
import java.rmi.Naming; public abstract class MessageClient { private MessageServer messageServer; public MessageClient() { System.out.println("Initializing Message Client"); } /** * Method used to connect to the message server * * @param serverName name of the server that contains the message server */ protected void connectToServer() { String serverName = getServerName(); try { String name = "//" + serverName + "/MessageServer"; messageServer = ((MessageServer) Naming.lookup(name)); } catch(Exception e) { System.out.println("Error connecting to Message Server. Exception is " + e); e.printStackTrace(); } } /** * Method used to send message to server * * @param phoneNum phone number to send message to * @param message message to send */ public boolean sendMessage(String phoneNum, String message) { try { return(messageServer.sendMessage(phoneNum,message)); } catch(Exception e) { System.out.println("Error Sending Message. Exception is " + e); e.printStackTrace(); return(false); } } public abstract String getServerName();} 这个类包含3个方法:sendMessage()方法将实际信息发送到服务器;connectServer()方法负责连接服务。在本例中,服务是一个RMI服务;getServerName()是一个抽象类,因为不同设备决定服务名字的方式不同。也就是说,任何继承自MessageClient的类都必须实现getServerName()方法。在开发与信息服务通信的客户端程序时,我们需要多重继承。我们的网络客户端是一个简单的用来接收来自表单的信息并将其传送到服务器的Servlet。为了完成上述任务,这个Servlet必须既继承HttpServlet又继承MessageClient。由于Java不允许这种继承行为,我让主类继承HttpServlet,让主类中的内部类继承MessageClient,然后外部类创建了内部类的一个实例。代码如下: 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); } }} 这种方法并不是真正的多重继承,因为我们使用了代理(例如:MessageClient被很多外部类继承,但并不是外部类本身),但是效果是一样的。虽然MessageClient能被单独的类扩展,但是使用内部类允许它访问外部类中的所有成员和方法。这让两个类之间的交互变得很容易。 这个例子只扩展了两个类,你当然可以使用这种技术扩展任意多类。 当然,其实这个信息传递服务的例子不用多重继承也能实现。如果MessageClient有一个能够接收服务服务名字的构造函数,那么getServerName()方法就可以不是抽象的。这意味着Web客户端可以不必继承MessageClient类。 最后提醒开发者,只有在有明确的理由,确定要使用上面技术时才使用,并且使用时要小心。因为多重继承使设计变得复杂,并且很容易被误用。