How to Create your OWN generic API

Joe

Thành viên VIP
21/1/13
3,011
1,335
113
Hi
Time to reactivate the Java Update folder before I scoot away... As you know, class Object is the only patriarch of all JAVA classes. Or in other words: all JAVA instances are the extension of Instance Object. Example: if you check the class String you will see this following hierarchy:
Module java.base
Package java.lang
Class String

The first is always java.lang.Object (where lang stands for LANGuage). Or let look at the class ArrayList:
Module java.base
Package java.util
Class ArrayList<E>

Again java.lang.Object is the patriarch of ArrayList.

In this sense you can see class Object is the GENERIC instance of all JAVA instances. So, where is the problem? Well, the problem is the casting if Object is used as the generic form. Example:
Java:
// Joe Nartca (C)
public class Test_1 {
  public static void main(String... a) throws Exception {
    Object o1 = add("Hello ",  "world");
    Object o2 = add(100, 200);
    if (o1.indexOf("world") > 0) {  // <<----error by compiler
      System.out.println("o1 contains world");
    }
    if (o2 == 300) {
      System.out.println("Result:"+o2);
    }
  }
  public static Object add(Object o1, Object o2) {
   return o1+o2;
  }
}
the devastated compiling result:
Code:
C:\links\java\CDJ>javac -g:none -d ./classes Test_1.java
Test_1.java:6: error: cannot find symbol
    if (o1.indexOf("world") > 0) {  // <<----error by compiler
          ^
  symbol:   method indexOf(String)
  location: variable o1 of type Object
Test_1.java:9: error: bad operand types for binary operator '=='
    if (o2 == 300) {
           ^
  first type:  Object
  second type: int
Test_1.java:14: error: bad operand types for binary operator '+'
   return o1+o2;
            ^
  first type:  Object
  second type: Object
3 errors

C:\links\java\CDJ>
With CASTING thing will go successfully:
Java:
// Joe Nartca (C)
public class Test_2 {
  public static void main(String... a) throws Exception {
    Object o1 = add("Hello ",  "world");
    Object o2 = add(100, 200);
    if (((String)o1).indexOf("world") > 0) {  // <<----CASTINGr
      System.out.println("o1 contains world");
    }
    if ((Integer)o2 == 300) { //<<-------CASTING
      System.out.println("Result:"+o2);
    }
  }
  public static Object add(Object o1, Object o2) {
    if (o1 instanceof String && o1 instanceof String)
      return (String)o1 + (String) o2;   //<<----CASTING
    if (o1 instanceof Integer && o1 instanceof Integer)
      return (Integer)o1 + (Integer) o2;   //<<----CASTING
    return null;
  }
}
and the result:
Code:
C:\links\java\CDJ>javac -g:none -d ./classes Test_2.java

C:\links\java\CDJ>java Test_2
o1 contains world
Result:300

C:\links\java\CDJ>
The examples show you that class Object is really the patriarch of all JAVA classes, but it's quite awkward to work with. Forgetting of CASTING always leads to troubles. So, JAVA invents two GENERIC expressions: E (for Element) and T (for Type) as we see in some JAVA classes. For example the above mentioned ArrayList. Their usage is quite simple: As a parameter or, as a return (click HERE for more). Example from ArrayList: the method toArray():
Java:
<T> T[]    toArray(T[] a)
The trick of this method is that the Type is given with the parameter so that the return will be with this type. Example:
Java:
ArrayList<String> al = new ArrayList<>();
...
String[] s = al.toArray(new String[al.size()]);
With the generic E and T you can easily create your OWN generic API. The instance Object is used as the generic object for the computing and processing so tat you don't have to toil steadily with CASTING. I show you hereunder an example with my "own" simple ListArray which works similarly with ArrayList, but without the implementation of List Interface, etc.
Java:
// Joe Nartca (C)
public class ListArray<E> {
  public ListArray( ) {
    this.capacity = MAX;
    objs = new Object[MAX];
    size = 0;
  }
  public ListArray(int capacity) {
    this.capacity = capacity > 0? capacity:MAX;
    objs = new Object[capacity];
    size = 0;
  }
  public int size() {
    return size;
  }
  public void clear() {
    size = 0;
  }
  public int indexOf(E element) {
    for (int i = 0; i < size; ++i) if (objs[i].equals(element)) return i;
    return -1;
  }
  public boolean contains(E element) {
    for (int i = 0; i < size; ++i) if (objs[i].equals(element)) return true;
    return false;
  }
  @SuppressWarnings("unchecked")
  public E get(int idx) {
    return (E)((idx < 0 || idx >= size)? null:objs[idx]);
  }
  // insert
  public void add(int idx, E element) {
    if (idx < 0 || idx > size) return;
    if (size >= capacity) grow();
    System.arraycopy(objs, 0, objs, 0, idx);
    System.arraycopy(objs, idx, objs, idx+1, size-idx);
    objs[idx] = element;
    ++size;
  }
  // append
  public void add(E element) {
    if (size >= capacity) grow();
    objs[size] = element;
    ++size;
  }
  // or set() in ArrayList
  public void replace(int idx, E element) {
    if (idx < 0 || idx >= size) return;
    objs[idx] = element;
  }
  public boolean remove(E element) {
    for (int i = 0; i < size; ++i) if (objs[i].equals(element)) {
      remove(i);
      return true;
    }
    return false;
  }
  @SuppressWarnings("unchecked")
  public E remove(int idx) {
    if (idx < 0 || idx >= size) return null;
    Object o = objs[idx];
    if (idx > 0) System.arraycopy(objs, 0, objs, 0, idx);
    System.arraycopy(objs, idx+1, objs, idx, size - idx - 1);
    --size;
    return (E) o;
  }
  @SuppressWarnings("unchecked")
  public Object[] toArray() {
    Object[] a = new Object[size];
    System.arraycopy(objs, 0, a, 0, size);
    return a;
  }
  public <T> T[] toArray(T[] a) {
    if (a.length < 0) return null;
    int len = a.length > size? size:a.length;
    System.arraycopy(objs, 0, a, 0, len);
    return a;
  }
  @SuppressWarnings("unchecked")
  public Object[] containsOf(E element) {
    ListArray la = new ListArray(size);
    for (Object o : objs)
    if (o instanceof String && element instanceof String &&
       ((String)o).indexOf((String)element) >= 0) la.add(o);
    if (la.size() == 0) return null;
    return la.toArray();
  }
  //-----------------------------------------------------
  private void grow() {
    int n = capacity+MAX;
    Object[] tmp = new Object[n];
    System.arraycopy(objs, 0, tmp, 0, capacity);
    capacity = n;
    objs = tmp;
  }
  private Object[] objs;
  private int MAX = 256, capacity, size;
}
Explanation:
  • the two constructors create an internal Object array with either the default MAX or the given capacity. ListArray is like ArrayList works with the generic E as ListArray<E>.
  • all methods size(), clear(), indexOf(), contains(), get(), add(), replace(), remove(), toArray() works similarly to ArrayList<E> methods. The replace() is similar to the set() of ArrayList.
  • method containsOf( ) works only when E is an instance of String, otherwise NULL is returned.
  • private method grow() increases the size of the internal array to avoid the Boundary Exception (see the add() method)

The test:
Java:
public class TestListArray {
  public static void main(String[] args) throws Exception {
    ListArray<String> la = new ListArray<>();
    la.add("Hello World");
    la.add(0, "Chao The Gioi");
    la.add("Allo Le Monde");
    la.add(2, "Hallo Welt");
    la.add("Buonos Dias");
    la.add("Good day");
    la.add("Bon jour");
    la.add("Guten Tag");
    la.add(3, "Xin Chao");
    //
    int n;
    byte[] bb = new byte[64];
    int le = System.lineSeparator().length();
    while (true && la.size() > 0) {
      System.out.print("Search Pattern:");
      n = System.in.read(bb)-le;
      String x = new String(bb, 0, n);
      Object[] fi = la.containsOf(x);
      if (fi != null) {
        if (fi.length == 1) System.out.println(fi.length+" Object contains pattern:"+x);
        else System.out.println(fi.length+" Objects contain pattern:"+x);
        for (Object s : fi) System.out.println(s);
      } else System.out.println(x+" is not existed in ListArray");
      Object[] So = la.toArray();
      String[] St = la.toArray(new String[la.size()]);
      System.out.println("\nAs Object:");
      for (Object s : So) System.out.println(s);
      System.out.println("\nAs String:");
      for (String s : St) System.out.println(s);
      //--------------------------------------
      System.out.println();
      System.out.print("Test get/remove/get\nWhat index (-1: exit):");
      n = System.in.read(bb)-le;
      n = Integer.parseInt(new String(bb, 0, n));
      if (n < 0) break;
      if (n >= la.size()) {
        System.out.println(n+" is too big. Size:"+la.size());
        continue;
      }
      System.out.println("\nget("+n+"):"+la.get(n));
      System.out.println("remove("+n+"):"+la.remove(n));
      System.out.println("\nget("+n+") again:"+la.get(n));
    }
  }
}
and the output:
generic.png
 
Sửa lần cuối: