2014/08/18

Using Scala in Eclipse Dynamic Web Project

既然 Scala 跟 Java 是遠房親戚,接下來我們想的是,如何在 Eclipse 開發網頁專案時,可以直接套用 Scala Library,並直接撰寫 Scala Code。

Step 1: Create a Dynamic Web Project

按照既有的步驟,建立一個 Dynamic Web Project。

Step 2: Add Scala Nature

在 project 上點右鍵,會出現一個選單,選擇 Configure,然後再點選 Add Scala Nature。

我們會看到 project 自動增加了 Scala Library [2.11.2],然後我們就可以寫 Scala 測試程式了

Step 3: ScalaFilter.scala

以 Scala 撰寫一個 filter: ScalaFilter.scala class。

package test

import javax.servlet.FilterConfig
import javax.servlet.ServletResponse
import javax.servlet.FilterChain
import javax.servlet.ServletRequest
import javax.servlet.ServletException
import javax.servlet.Filter
import java.util.Date

class ScalaFilter extends Object with Filter {

  @throws(classOf[ServletException])
  def init(filterConfig: FilterConfig): Unit = {
    println("ScalaFilter: init()");
  }

  @throws(classOf[_root_.java.io.IOException])
  @throws(classOf[ServletException])
  def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = {
    println("ScalaFilter: doFilter()");

    response.getWriter().write("> The time now is " + new Date);
  }

  def destroy(): Unit = {
    println("ScalaFilter: destroy()");
  }

}

Step 4: web.xml

在 web.xml 裡面把 Filter 設定好

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0">
    <display-name>test</display-name>

    <filter>
        <filter-name>ScalaFilter</filter-name>
        <filter-class>test.ScalaFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>ScalaFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Step 5: Testing

直接將 project Run on Server 測試看看,結果發生了ClassNotFound 的錯誤,很明顯就是 Deploy 的時候,沒有把 Scala Library 放到 lib 目錄裡面。

解決方式很簡單,到 scala SDK 的 lib 目錄,把以下四個 jar 複製到 project 的 WebContent/WEB-INF/lib 目錄中。

scala-actors-2.11.0.jar
scala-library.jar
scala-reflect.jar
scala-swing_2.11-1.0.1.jar

再一次將 project Run on Server 測試看看,就成功了。

2014/08/11

Java 8 Default Methods

Java 8為介面(Interface)引進了一個新的特性,名為Default Method,講白一點其實就是可以在介面內寫一些已經實作的方法。也就是說,在Java 8介面不再只是開規格給類別實作而已,他現在還可以提供一些已經實作的方法給人使用。

如何宣告與使用?

Default Method的宣告方法很簡單,只要加上default關鍵字即可,需要注意的是,在介面內的方法必須宣告為public的。可以參考以下程式:

public interface MyInterfaceI {
    default public void testDefaultMethod(int i) {
        System.out.println("default method test2()");
        if(i > 3) {
            System.out.println(">3");
        } else {
            System.out.println("<=3");
        }
    }
}

要使用該Default Method時,只要透過實現該介面的類別,呼叫該Default Method即可,如下:

class MyInterfaceImpl implements MyInterfaceI {
}

public class MainClass {
    public static void main(String[] args) {
        MyInterfaceImpl impl = new MyInterfaceImpl();
        impl.testDefaultMethod(2); // output: <=3
    }
}

需要注意?

  • 繼承介面時,若父介面有Default Method,則子介面會繼承他。如底下的範例:

      public class InterfaceImpl implements InterfaceSon{
          public static void main(String[] args) {
              InterfaceSon son = new InterfaceImpl();
              son.test(); // output => InterfaceParent
          }
      }
    
      interface InterfaceParent {
          public default void test() {
              System.out.println("InterfaceParent");
          }
      }
    
      interface InterfaceSon extends InterfaceParent{
      }
    
  • Default Method是可以被覆寫(override)的,因此繼承之後你可以將之覆寫掉。如底下的範例,將繼承的介面Default Method覆寫掉,則程式在執行時就會跑覆寫的那個版本:

      public class InterfaceImpl implements InterfaceSon{
          public static void main(String[] args) {
              InterfaceSon son = new InterfaceImpl();
              son.test(); // output => InterfaceSon
          }
      }
    
      interface InterfaceParent {
          public default void test() {
              System.out.println("InterfaceParent");
          }
      }
    
      interface InterfaceSon extends InterfaceParent{
          public default void test() {
              System.out.println("InterfaceSon");
          }
      }
    
  • 繼承之後,若不想要使用原本父類別的Default Method,則可宣告成抽象方法,讓實作類別實作該方法。可以參考底下範例:

      public class InterfaceImpl implements InterfaceSon{
          public static void main(String[] args) {
              InterfaceSon son = new InterfaceImpl();
              son.test(); // output => InterfaceImpl
          }
    
          @Override
          public void test() {
              System.out.println("InterfaceImpl");
          }
      }
    
      interface InterfaceParent {
          public default void test() {
              System.out.println("InterfaceParent");
          }
      }
    
      interface InterfaceSon extends InterfaceParent{
          public void test();
      }
    
  • 如果類別要實作的介面,剛好有兩個相同名稱,相同輸入參數的Default Method,則編譯器會出錯。可以參考底下程式:

      public class InterfaceImpl implements Interface1, Interface2 {
          // 編譯器會報以下錯誤
          // Duplicate default methods named test with the 
          // parameters () and () are inherited from the types 
          // Interface2 and Interface1
      }
    
      interface Interface1 {
          public default void test() {
              System.out.println("Interface1");
          }
      }
    
      interface Interface2 {
          public default void test() {
              System.out.println("Interface2");
          }
      }
    
  • 上面情況發生時,可以用覆寫來把Default Method蓋掉,在覆寫時還可以用super關鍵字來指定要用哪個實作的Default Method,可以參考底下程式:

      public class InterfaceImpl implements Interface1, Interface2{
          public void test() {
              Interface1.super.test(); // output => Interface1
          }
      }
    
      interface Interface1 {
          public default void test() {
              System.out.println("Interface1");
          }
      }
    
      interface Interface2 {
          public default void test() {
              System.out.println("Interface2");
          }
      }
    
  • Default Method在使用時是沒有狀態的,所謂的狀態指的是在一般類別與抽象(abstract)類別裡,能在類別的欄位(field)存放資料,如存放布林值、整數、字串等等的資料,來代表此類別的狀態,而Default Method是存在於介面內的,介面是不允許存放資料欄位的,因此它唯一能操作的資料,只有透過參數傳進來的資料而已。

Static Methods

此外除了Default Method,現在在Java 8內,你也可以寫Static Method了,他的宣告方法就如同在一般類別內宣告static method一樣,如下:

public interface MyInterfaceI {
    public static void invokeStaticMethod() {
        System.out.println("It is static method!");
    }
}

使用的話如下:

public class MainClass {
    public static void main(String[] args) {
        MyInterfaceI.invokeStaticMethod(); // output: It is static method!
    }
}

Interface Default Methods in Java 8

The Java Tutorials - Default Methods

Hello Scala

因應函數式語言的演化趨勢,Scala 選擇不再繼續用拙劣的 Java 語法加入 Functional Language 的特性,而是用另一個方式強化 Java 語言,以 Scala 撰寫的程式,可以直接在 JVM 裡面運作,這代表 Scala 可以直接使用既有廣大的 Java 函式庫,wiki 裡面第一句話,就明確地定位了 Scala: Scala is an object-functional programming and scripting language for general software applications. 對於 Java Programmer 來說,可以先閱讀這一篇文章,了解 Scala:Scala vs Java:兩者間的差異與相似處

安裝 scala 開發環境,首先是需要把 JDK 跟 scala SDK 裝好,第二步是安裝 scala IDE,第三步撰寫 Hello World 程式,就等於是把 scala 環境準備好了。

Step 1: SDK

到網站 http://www.oracle.com/technetwork/java/javase/downloads/index.html 下載並安裝 JDK,目前我們還是習慣用 JDK 7。

JDK 安裝完成後,記得要自己設定環境變數 JAVA_HOME 到安裝的目錄,並增加 %JAVA_HOME%\bin 到 PATH 環境變數中。

到網站 http://www.scala-lang.org/download/ 下載並安裝 Scala,目前的版本為 2.11.2,安裝完成後,程式會自動把 scala 的 bin 目錄設定到 PATH 裡面。

Step 2: IDE

因為我們已經裝了 Eclipse,根據網站 http://scala-ide.org/download/current.html 的說明,我們使用 plugin 的方式安裝IDE。

把 2.11.2 版的 IDE update site http://download.scala-ide.org/sdk/helium/e38/scala211/stable/site 增加到 Eclipse 軟體列表中就可以進行安裝,除了 Source 不安裝之外,我點選了其他四個項目。

Step 3: Hello World

點擊 File -> New -> New Scala Project 可新增一個 Hello World scala project。

查看 project library 的地方會發現,project 把 JDK 跟 scala SDK 的 libraries 都加進來了。

新增一個 HelloWorld scala object

package test

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello World")
  }
}

在 scala object 上點右鍵 -> Run As Scala Application,就可以在 console 上看到列印出來的字串 Hello World。

Step 4: Command Line

如果不借助 IDE 的協助,我們可以直接在 Command Line 環境進行編譯與執行。

先打開 windows command prompt 切換到 project 的 src 目錄,用以下指令編譯 object

scalac test\HelloWorld.scala

會在 test 目錄中看到 HelloWorld.class 與 HelloWorld$.class 兩個 class。

再用以下指令執行

scala -classpath . test.HelloWorld