среда, 3 ноября 2010 г.

GWT: Обманываем Java

Вопрос на засыпку, что выведет эта программа:

  1. public class Inheritance {
  2.     private static class A {
  3.         public void tt() {
  4.             System.out.println("a");
  5.         }
  6.     }
  7.  
  8.     private static class B extends A {
  9.         public void tt() {
  10.             System.out.println("b");
  11.         }
  12.     }
  13.  
  14.     public static void main(String... args) {
  15.         A a = new A();
  16.         a.tt();
  17.         B b = (B) a;
  18.         b.tt();
  19.     }
  20. }

Она скомпилируется, но при запуске кинет эксепшн:

a
Exception in thread "main" java.lang.ClassCastException: Inheritance$A cannot be cast to Inheritance$B
at Inheritance.main(Inheritance.java:18)


Все верно... для Java.

Однако, в GWT можно так делать. Причем, даже нужно, потому что других способов создать иерархию объектов из JavaScriptObject невозможно, ведь инстанцирование их напрямую запрещено, что исключает использование фабрик. К примеру:


  1. import com.google.gwt.core.client.JavaScriptObject;
  2. import com.google.gwt.core.client.JsArray;
  3. import com.google.gwt.user.client.Window;
  4.  
  5. public class Test {
  6.     private static class A extends JavaScriptObject {
  7.         protected A() {
  8.         }
  9.  
  10.         public final void amethod() {
  11.             Window.alert("A");
  12.         }
  13.  
  14.         public final native boolean isB() /*-{
  15.             return this.is_b;
  16.         }-*/;
  17.     }
  18.  
  19.     private static class B extends A {
  20.         protected B() {
  21.         }
  22.  
  23.         public final void bmethod() {
  24.             Window.alert("B");
  25.         }
  26.     }
  27.  
  28.     public void test() {
  29.         JsArray<A> objects = getObjects();
  30.         for (int i = 0; i < objects.length(); i++) {
  31.             A a = objects.get(i);
  32.             if (a.isB()) {
  33.                 ((B) a).bmethod();
  34.             } else {
  35.                 a.amethod();
  36.             }
  37.         }
  38.     }
  39.  
  40.     private static final native JsArray<A> getObjects() /*-{
  41.         return [{is_b: false}, {is_b: true}];
  42.     }-*/;
  43. }

Это можно объяснить тем, что скомпилированный код - уже не Java, а Javascript. Конечно, нельзя использовать наследование по полной - перегружать методы например, т.к. они или класс должны быть "final", но это позволяет разнести логику наследуемых сущностей по классам, сделать код более читаемым и проще поддерживаемым.