Multiple Inheritance in JAVA?
Java
68
White

Joe viết ngày 10/09/2017

Hi

Java developers certainly know the famous dogma Single Inheritance. It sets the rule that every JAVA object is always derived from JAVA class Object and it can be only heir of ONE other JAVA object. Example:

public class Father {
      ...
}

is the same as

 public class Father extends Object {
      ...
}

and the grandchild of Object, child of Father

public class GrandChild extends Father {
      ...
}

C++ allows multiple Inheritance. Any C++ object can have multiple ancestors and that complicates everything: bug-prone when several ancestors share the same member names. In this case it's easily for developers to fall into some ambiguity pitfalls. Example

class A {
public:
  int i;
  int j;
  void foo(int) { }
};

class B {
public:
  int j;
  void foo() { }
};

class C : public A, public B {
public:
  int i;
};

int main() {
  C cobj;
  C *cptr = &cobj;
  cptr->i = 5;
  cptr->j = 10;    // pitfall 1: Which j? Of A or of B
  cobj.foo();      // pitfall 2: which foo()? Of A or of B
  ...
}

As JAVA was developed in 1995 SUN development team around James Gosling quickly realized that such hidden pitfalls were hard to detect, easily committed by even experienced developers. The pitfalls made the software becoming unreliable. That was the reason why JAVA pegs on Single Inheritance.

As JAVA started to fly. Around the year 2000 Microsoft was scared by the success of JAVA and started to create its own JAVA-like OOPL that was known as C# (pronounced: C sharp). A JAVA clone was born with some enhancements.

Back to JAVA, life is not a free lunch. Sometimes an object needs to have some more additional "characteristics" than only those it inherited. But JAVA does not allow object to inherit several different fathers. A dead-end, isn't it? Well, Java offers the interface, a simple and genius solution.

So, what is an Interface? The word consists of inter (between) and face. Also: between the faces...like this optical illusion of 2 faces, or of a vase?

alt

I'm sure that JAVA developers know exactly what JAVA Interface is and what-for it is used. But have they ever thought about the most relevant aspect that interface is the backdoor of JAVA to multiple inheritance? Yes, it is. Interface allows JAVA object to acquire some other characteristics that don't exist in the line of its progenitors. The word "acquire" is more appropriate than "inherit". However, the purpose is the same: to amend, to enhance a java object which is hedged by single-inheritance paradigm. The acquired characteristics or properties must be implemented, but free according to different requirements. Because JAVA interface is a Java object. And that means a JAVA interface can inherit, too. The only difference is that a java interface can have several (different) fathers. Example

import java.awt.event.*;
public interface MyInterface extends ActionListener, KeyListener {
   public void print();
}

MyInterface is an extension of ActionListener and KeyListener. It inherits also all ActionListener and KeyListener properties.

The backdoor Interface opens a variety of opportunities for developers to create the fanciest objects which were impossible with Single-Inheritance. Example: I want to monitor a certain directory and a certain file (of this directory) which are accessed by the others (LAN/WAN or Web) so that I can trace all activities back to manipulations, creations or deletions. The approach is a straight forward work.

Firstly I need to define the activities with a JAVA interface

public interface Monitoring {
    /**
    @param msg String, Event Message
    */
    public void dirChanged(String msg);
    /**
    @param mode int, 0: monitored file was modified, 1, 2: some data was deleted or inserted
    */
    public void fileChanged(int mode);
}

then a Monitor that should run autonomously. That can only be done by a Thread (or Task). Let take Thread as the father of Monitor. Monitor is bound to some Monitoring objects. If anything happens to one of the monitoring objects the appropriated method dirChanged() or fileChanged() of that object is invoked (callback).

// Monitor is heir of Thread
// Joe T. Nartca (C)
import java.util.concurrent.*;
import java.nio.file.*;
import java.util.*;
import java.io.*;
//
public class Monitor extends Thread {
    public Monitor(String dir, String fileName) {
        this.fileName = fileName;
        this.dir = dir;
    }
    private List<Monitoring> list = new CopyOnWriteArrayList<Monitoring>();
    private String dir, fileName;
    private boolean end = false;
    // add object that'd be monitored
    public void add(Monitoring mom) {
        list.add(mom);
    }        
    // remove the monitored object
    public void remove(Monitoring mom) {
        list.remove(mom);
    }
    public void exit( ) {
        end = true;
    }
    public void run( ) {
        try {
            int n = 0;
            byte[] mem = null;
            FileInputStream fi; // load the content of monitored file
            String abs = fileName == null? null:dir+File.separator+fileName;
            if (abs != null) {
                fi = new FileInputStream(abs);
                mem = new byte[fi.available()];
                n = fi.read(mem);
                fi.close();
            } // using JAVA standard API watchService
            WatchService ws = FileSystems.getDefault().newWatchService();
            WatchKey wk = Paths.get(dir).register(ws,
                                StandardWatchEventKinds.ENTRY_CREATE,
                                StandardWatchEventKinds.ENTRY_DELETE,
                                StandardWatchEventKinds.ENTRY_MODIFY);
            while (!end) { // loop till exit() is called
                for (WatchEvent<?> event : wk.pollEvents()) {
                    WatchEvent.Kind<?> kind = event.kind();
                    String fn = dir + File.separator + ((WatchEvent<Path>)event).context().toFile();
                    if (kind == StandardWatchEventKinds.ENTRY_MODIFY) { // file is changed
                        if (abs != null && abs.equals(fn)) {
                            fi = new FileInputStream(abs);
                            byte[] mod = new byte[fi.available()];
                            fi.read(mod);
                            fi.close();
                            boolean dif = false;
                            int len1 = mem.length;
                            int len2 = mod.length;
                            for (int i = 0; i < len1 && i < len2; ++i)
                            if (mem[i] != mod[i]) { // different?
                                dif = true;
                                break;
                            }
                            if (dif) { // callback
                                mem = new byte[len2];
                                System.arraycopy(mod, 0, mem, 0, len2);
                                int type = (len1 > len2)? 0:(len1 < len2)? 1: 2;
                                for (Monitoring mom : list) mom.fileChanged(type);
                                type = -1;
                            }
                        }
                    // callback...Directory is changed
                    } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
                        for (Monitoring mom : list)
                            mom.dirChanged("Deleted file: "+fn);
                    } else if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
                        for (Monitoring mom : list)
                            mom.fileChanged("Created new file: "+fn);
                    }
                }
                wk.reset();
            } 
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

now I can implement the "WatchingService" with 3 fathers: JFrame, Monitoring and ActionListener.

// Joe T. Nartca (C)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// WatchingService has TWO ancestors: JFrame and Monitoring
public class WatchingService extends JFrame 
implements Monitoring, ActionListener {
    public WatchingService(String dir, String fileName) {
        this.dir = dir;
        this.fileName = fileName;
        // start the Monitor...
        Monitor mon = new Monitor(dir, fileName);
        mon.add(this); // bind this app to Monitor
        mon.start();
        // GUI settings
        setTitle("Watching:"+dir+" and file:"+fileName);
        jta = new JTextArea(20, 42);
        jta.setFont(new Font("Veranda",Font.BOLD, 12));
        jta.setForeground(Color.yellow);
        jta.setBackground(Color.black);
        jta.setEditable(false);
        //
        JScrollPane jsp = new JScrollPane(jta); 
        jta.setWrapStyleWord(true);
        jsp.setAutoscrolls(true);
        jta.setLineWrap(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add("Center", jsp);
        //      
        JPanel jp = new JPanel();
        JButton exit = new JButton("Exit");
        exit.addActionListener(this);
        jp.add(exit);
        add("South", jp);
        //
        setSize(600, 400);
        setResizable(false);
        setVisible(true);
    }
    // implemented the required method of ActionListener
    public void actionPerformed(ActionEvent e) {
        System.exit(0);
    }
    // implemented the required method dirChanged() of Monitoring
    public void dirChanged(String msg) {
        jta.append(msg+"\n");
        jta.setCaretPosition(jta.getDocument().getLength());
    }
    // implemented the required method fileChanged() of Monitoring
    public void fileChanged(int mode) {
        if (mode == 2) 
             jta.append("Content of "+fileName+" of Directory:"+dir+" was modified.\n");
        else if (mode == 1)
             jta.append("Some Data Content of "+fileName+" of Directory:"+dir+" was inserted.\n");
        else if (mode == 0) jta.append("Some Data content of "+fileName+" of Directory:"+dir+" was deleted.\n");
        jta.setCaretPosition(jta.getDocument().getLength());
    }
    //
    public static void main(String... args) throws Exception {
        if (args.length != 2) {
            JOptionPane.showMessageDialog(null, "Usage: java WatchingService DirName FileName");
            System.exit(0);
        }
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
        WatchingService ws = new WatchingService(args[0], args[1]);
    }
    private JTextArea jta;
    private String dir, fileName;
}

This example demonstrates how WatchingService can have three different fathers. One is the extension of JFrame, the others are Monitoring and ActionListener. The interfaces force WatchingService to implement the required methods: actionPerfromed() of ActionListener, dirChanged() and fileChanged() of Monitoring.

WatchingService shows you how JAVA elegantly circumvents the restriction of Single Inheritance using the backdoor interface. However, interface always requires an implementation of all defined methods and that can cause in some case a lot of redundancy which could lead to some hidden bugs. Especially when some methods could be implemented once, no need to re-implement every time by each heir. In such a case abstract object is the most suited solution. Only the declared abstract methods need to be implemented. However, heirs of abstract object fall back to the Single-Inheritance restriction.

I am sure that you, JAVA or Non-JAVA developer, have ever heard of Design Patterns (DP). Aside the GoF (Gang of Four) bible, JAVA has already provided numerous DP packages that you probably aren't aware of. Let me give you some DP examples that base on interfaces and on abstract objects.

  • Single or multiple interface: ArrayList, LinkList, Stack, Vector, etc. are List-Interface implementations.
  • Abstract object: All JAVA IO streams are derived from two abstract objects: InputStream and OutputStream. Only the method int read() and write(int) must be implemented (native).
Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Joe

8 bài viết.
35 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
22 9
Fuzzy Logic and Machine Learning Hi First of all: I apologize everyone for my writing in English. I come to this site because someone of Daynhauh...
Joe viết 6 tháng trước
22 9
White
9 3
Hi Giaosucan wrote an interesting article and a good technical overview about Bitcoin (Blockchain). If anyone does have interest on the Blockchain...
Joe viết 6 tháng trước
9 3
White
4 0
Hi People, especially the laymen, usually fantasize about Artificial Intelligence (AI). They wildly dream up the scenario that AI bots do our hous...
Joe viết 5 tháng trước
4 0
Bài viết liên quan
White
0 0
Trong bài viết này, một số hình ảnh hoặc nọi dung có thể bị thiếu do quá trình chế bản. Vui lòng xem nội dung ở blog gốc sau: (Link) (Link), chúng...
programmerit viết hơn 2 năm trước
0 0
Male avatar
8 5
Facade Design Patern Facade Patern thuộc vào họ mô hình cấu trúc (structural patern). Facade patern phát biểu rằng : "just provide a unified an...
DuongVanTien viết hơn 1 năm trước
8 5
White
0 2
(Link), mình đã nêu ra các phần mềm chính (portal, sdk và ide). Trong phần này, mình sẽ hướng dẫn cụ thể cài đặt môi trường máy chủ và môi trường l...
programmerit viết hơn 2 năm trước
0 2
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
8 bài viết.
35 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!