Swiftをチラ見しよう

なんかアップルから新しい言語、出たみたいですよ?

Welcome to Swift

どうせ多分一ヶ月後には忘れると思うけど、言語を一巡りしてみよう。体力ないのでLanguage Guideまでで失礼します。

ちなみに現時点において動作環境を手に入れるには、iOSデベロッパ契約が必要です。僕はありません。

このエントリは全て憶測で書いています。

このエントリは全て憶測で書いています。

このエントリは全て憶測で書いています。

このエントリは全て憶測で書いています。

ひとめぐり

(途中、私自身にしか通じないような表現が出てきます。)

// コメント
/* マルチラインコメント */
/* /* ネストしたコメントも可能! */ */

// main関数不要
println("Hello world") // C系にありがちなセミコロンも要らない

//// 変数 
// Swiftは強い型付き。
var v = 10 (型宣言しないと推論される)
var v: Double = 10.0 
var (x, y) = (1, 2)
(y, x) = (x, y) // ちなみに = 自体は値を返さないそうだ

// 定数
let c = 10

//// 文字列への埋め込み式 \(...)
let message = "I need to learn \( c ) languages."

///// コレクション
// 配列 (構造体の一種)
let list = ["apple", "bacon", "cheese"] // let list: String[] あるいはlet list: Array<String>、型[]は Array<型>の短い書き方
let intList = 0..10 // 0, 1, 2, 3, .. , 9
let intHalfList = 0...10 // 0, 1, 2, 3 .. , 10
var list = [1, 2, 3]
var doubleList = list + list // リストの結合
var mixture: Any[] = [0, "asdf", (1, 2)] // なんでもあり型はAny
list[0] // もちろんインデックスでアクセス可能
list[0..1] = ["BIG APPLE", "BACON PIE"] // 複数要素代入可能
let emptyIntList = Int[]() // 空リスト
// 構造体の一種とされているが、配列は代入によってコピーされないし、代入元とリスト内容を共にする。
// インデックスへの代入も共に影響をうける。
// しかし、配列に値を追加した場合 or array.unshare() した場合に配列がコピーされたものにいれかわる。
// 配列を代入するときに array.copy() で代入すれば最初からコピーされたものになる。

// ディクショナリ(マップ 構造体の一種)
let map = [
"New York": "The Big Apple",
"Las Vegas": "Sin City", // マップの末尾が , で終わっていてもいい
] // let map: Dictionary<String, String>
// 構造体の一種なので、ディクショナリを代入したり関数に渡したりするとディクショナリは値型のようにコピーされる。

//// 型

// deftype的な
typealias EnoughMemoryForHumanLife = UInt16

// String
// 値型の一種。
// \0, \\, \t, \r, \n, \", \'  などバックスラッシュ記法もある。
// \xnn (2桁 16進法), \unnnn (4桁 16進法), \Unnnnnnnn (8桁 16進法)
"🐶🐱" // 絵文字OK。
// そしてUTF-16エンコーディングなどされない。絵文字1文字も正確に1文字である。
// 文字数のカウント、任意位置の文字の取り出しは効率がO(n)であり、巨大文字列の文字数カウントはコストがかかる。
// カウントのコストはCと同じ。
// 1文字ずつ先頭からインデックスでイテレートしたらO(n^2)。恐怖。for-in文を使うこと。
"" // == String() 空文字

// Character
// StringはCharacterのリスト。
let c: Character = "ユニコード1文字"

// 数
// Javaとだいたい同じ
10
1000_000.000_001 // 1000000.000001
0b01010 // 10
0o12 // 10
0xc // 10

// Int8 = signed char,  UInt8 = unsigned char 相当, UInt16 なども
// キャストは構造体newと同じく キャスト後の型(値) でOK

// Bool(ean)
// if 文はBoolしかうけつけない
true
false

// Optional値。nullableってことです。
var optionalValue: String? = findSomething()
optionalValue! // これで中身アクセス
if optionalValue { }  // if文の条件とかにそのまま使える。Boolコンテキストで使える?
// Optionalにはnilを代入できる。nullではなくnilです。
// nilには厳しく、optional以外はnilを扱えません。
optionalValue = nil

// オブジェクトの作成は "クラス()" で。
let newObject = Class()

// タプル
let tupleValue: (String, Int) = ("あ", 1)
let namedTuple = (hiragana: "あ", at: 1) // namedTuple.hiragana でアクセス可能。名前も型の一部みたいな。
func returnTuple() -> (name: String, age: Int) {
  return ("ほげほげ", 100)
}
// 関数
// パラメータに=つければデフォルト値
func subtract(left: Int, right = 0) -> Int {
  return left - right
}

// あるいは
func subtract(l left: Int, r right: Int) { ... }
func subtract(#l: Int, #r: Int) -> Int { ... }
// で、呼ぶときはsubtract(l: 20, r: 10) といったように名前付き引数で指定可能。

// 引数変更したい人はパラメータにvarつけりゃいいよ
func foo(var youCanAssignThisVar: string) { ... }

// 参照渡ししたい人はパラメータにinoutつけりゃいいよ
func inc(inout i: Int){ return i++ }
var foo = 3
inc(foo)
foo // == 3
inc(&foo)
foo // == 4

// 関数は値として使える。レキシカルスコープを持つ。
// "-> 型" で関数の返りの型
func greeting(name: String) -> String {
  return "Hey, \( name )-san."
}
// func greeting(name: String...) と書けば可変長引数(VLA)

// レキシカルスコープかつ関数は値として使えるのでこういうのも可能
func curryAdd(x: Int) -> (Int -> Int) {
  func internalAdd(y: Int) -> Int {
    return x + y
  }
  return internalAdd
}
curryAdd(10)(20) // => 30

// 関数名を省略して、こうも書ける
func curryAdd(x: Int) -> (Int -> Int) {
  (y: Int) -> Int in
  return x + y
}

// 型がわかっている場合はこれも可能
func double(intList: Int[]) {
  intList.map({ eachInt in each 2 * eachInt })
}
double([1..100])

// クロージャ内の引数は "$数字" でも参照可能(ゼロベース)
{ $0 > $1 }

//// 演算子オーバーロード
// 中置演算子 + をオーバーロード       
@infix func + (left: MyType, right: MyType) -> MyType { return ... }
// ほか @prefix func - (a: A) -> A { return ... }
// @assignment func += (inout left: A, right: A){ left = ... }
// @prefix @assignment func ++ (inout a: A) -> A { a = ... return a }
// @infix func ==, @infix func != 

// カスタム演算子の定義(のち、演算子オーバーロード)
// prefix / infix / postfix で出現位置を指定:
operator prefix +++ {}

// クラス。C#と同じく対立する値型の概念として構造体がある。
// 継承関係は : のあとに親クラスでOK
class Hoge : Fuga{
  // クラスフィールド
  var myName
  @lazy var myStrictName = getMySecretNameFromFarDB() // @lazy使うと最初のアクセス時まで初期化を遅らせられる

  // コンストラクタはinit, thisはself, superはsuper
  init(name: String) {
    self.myName = "Pretty \(name)-chan"
  } // deinit() がデストラクタ

  // 普通のメソッド。オーバーライドを防ぎたい場合は@final fun thisIsAMethod() みたいにする。
  func thisIsAMethod() -> String {
    return myName
  }

  override func overwrittenMethod() -> Int {
    // オーバーライドしたメソッドはoverrideと絶対に書かなければならない
  }

  class func methodBelongingToHoge() {
    // Javaのstaticメソッド. クラスメソッド. Hoge.methodBelongingToHoge()
    self // ここでのselfは型自体
  }

  var height: Double
  var weight: Double
  // C#のようなプロパティ構文
  // willSet {}, didSet {} でプロパティ設定前後の動作を指定可能
  // overrideしてる場合は override var bmi: Double { ... } みたいにしてね
  var bmi: Double {
    get {
      return weight / (height * height)
    }
    set {
      // newValueとして代入文の右辺の値を貰える
      height = sqrt( 1 / ( newValue / weight ) )
      // newValueが気に入らなければ set( newBMI ) とかすればnewBMIという名前にできる
    }
  }
  // get { ... } のみなら、 get というキーワードは省略可能

  // subscript
  // 要するに hoge[...] のインデックス指定のアクセサ構文。
  // 1個以上の引数を指定可能.
  subscript(index: Int) -> String {
    get { ... } // indexを使って内部的なリストから値引っ張るなり
    set { ... } // indexを使って内部的なリストに値を引っ張り込むなり
  }

  // Nested types
  // 中に入れ子のenum / structure / classが定義可能
  // (たぶんJavaみたいに複雑な非static/staticクラスの区分けみたいのは無く、一律staticな感じかも)

}
// プロパティ/メソッドは . でアクセスできる
foo.bar
foo.bar()
// Optionalな変数の場合は ?. でアクセスできる
optionalFoo?.bar

// 構造体
// C#と似たらしい感じ。オブジェクトなら参照がコピーされるところで値ごとコピーされる。
struct Color {
  var r: Int
  var g: Int
  var b: Int
  func distance() -> Double {
    return sqrt( r * r + g * g + b * b)
  }

  // static変数もちたければどうぞ
  static var asdf = 1
  static func modifyAsdf(newVal: Int) {
    asdf = newVal
  }

  // 構造体のプロパティを更新するものはmutating修飾子が必要
  mutating func superRed() {
    self.r = 255
    self.g = 0
    self.b = 0
  }

  // そのままself自体入れ替えも可能
  mutating func superRed2(){
    self = Color(255, 0, 0)
  }

  // func init(){ } コンストラクタもあり
}
var c: Color = Color(255, 255, 255)

// Enum
enum Foods: Int {
  case Apple = 1
  case Bacon, Cheese, Donuts
  func tastes() -> String{
    switch self { // Enumのswitchは
    case .Apple, .Donuts:
      return "Sweet"
    case .Bacon, .Cheese:
      return "Salty"
    default:
      return "What the heck is this?"
    }
  }
  // enumもmutating持てる
  mutating func iAmAnApple() {
    self = Apple
  }
}
Foods.Apple.tastes() // "Sweet"
Foods.Apple.toRaw() // 1
fromOne: Foods? = Foods.fromRaw(1) // == Foods.Apple が来るだろうけどOptional
// 代数的データ型 (Algebraic Data Type) みたいな? のも可能
enum TreeEntry {
  case Node(TreeEntry, TreeEntry)
  case Leaf(String)
}
var t:TreeEntry = TreeEntry.Node(TreeEntry.Leaf("Big bro"), TreeEntry.Node(TreeEntry.Leaf("Me"), TreeEntry.Leaf("You")))

func traverse(t: TreeEntry){
  switch t {
  case let .Node(leftNode, rightNode):
     traverse(leftNode)
     traverse(rightNode)
  case let .Leaf(name):
     println("Found \(name).")
  } 
}

//// Protocol
// インタフェースみたいなの
protocol HavingName {
  var name: String { get }
}
class Cat: HavingName {
  // プロトコルの実装
  var name: String = "吾輩は猫である"
}
// プロトコルを複数実装したオブジェクトの型は obj: protocol<ProtocolA, ProtocolB> みたいに扱うことも可能で
// それぞれ obj.protocolAMember obj.protocolBMember みたいにそのままプロトコルメンバにアクセス可能   

//// Extension
// C#のExtension, Rubyにも似たようなのあったね
extension Int { // よく Int : ProtocolName1, ProtocolName2 のようにプロトコルをおっかぶせたりするらしい
  func double() -> Int{
    return self * 2
  }
}
let pi = 3
pi.double() // == 6

//// Generics
enum Tree<LeafType> {
  case Node(Tree<LeafType> left, Tree<LeafType> right)
  case Leaf(LeafType)
}
func findAll<LeafType>(t: Tree<LeafType>) -> LeafType[] {
  switch(t) {
    case let t.Node(l, r):
      return findAll(l) + findAll(r)
    case let t.Leaf(v):
      return [v]
  }
}
// 型の制約
// <A, B, C where ... > というように where 以降に書く。細かい制約もかけるっぽい。

//// 演算子 だいたい普通の演算子は使える。
i++
i += 10
9.2 % 3.0 // == 0.2 doubleの除算もできる
bool ? true-case : false-case // いわゆる三項演算子
a === b // アイデンティティ比較
a is Type // a は Typeを実装しているか?
a as DownCastType // a を DownCastTypeとしてキャスト
a as? DownCastType // a を DownCastType?としてキャスト(キャストできなきゃnil)

//// 制御構造
//// if 
if Bool式 { } else if { } else { }

//// Optional Binding(if-let 式)

if let foo = optionalな値を返す式 {
  message = "The found value is \(foo)" // ちゃんと値があった場合の分岐 
} else {
  message = "The value is not found." // デフォルト値処理とかこっちでやればいいんじゃない?
}

//// for-in
for member in memberList {
}

// for-in+分配。マップのエントリごとループなど。
let map = [ "New York": "The Big Apple", "Las Vegas": "Sin City"] 
for (cityName, cityNickname) in map {
}

// 普通のfor文
for var i = 0; i < 10; ++i {
}

// while / do-while
while i > 0 { }
do { } while i > 0

// switch
switch customerName { // スイッチ対象の値の型はいろいろできるらしい
case "knjname":
  println("All our property are belong to you.")
case "knjname2": // 基本的にfall-throughしない
  println("You're faking.")
case "knjname3", "knjname4" // 複数の値にマッチさせたい場合
  println("3 or 4")
case "knjname5"
  println("5 is good and also ...")
  fallthrough // fall throughしたい違いのわかる大人用
case "knjname6"
  println("6 is better.")
default: 
  println("Your life is under knjname's control.")
}

// こういうこともできる
switch hoge {
  case 0 as Int:
    ...
  case 0 as Double:
    ...
  case is Double:
    ...
  case let (x, y) as (String, String):
    ...
  case 0..10:
}

case 1..100: // みたいなのも数値の振り分けには使える

// ラベル
// Javaと同じ。制御構造にラベルをつけてbreak/continueに飛べる
outside: while ... {
  while ... {
    if(...) break outside
  }
}

感想

  • とーってもC#っぽい。Optionalとか値型とかプロパティとか。ここらへんの感想は人によって違うんだろうけれども。
  • 正直、言語としてパクりだらけだとか、新しいアイディアがないとか、そういうのはわかりますけど、ちょっと違うと思うんです。既存に比べて、使いやすい、わかりやすい、これ大事じゃないでしょうか。
  • 文字列は文字のリストなのは普通だけど、文字がユニコード1文字というのはすごい。UTF-16でお茶を濁す言語とちがって、🐶(絵文字)が1文字です。当然文字数カウントはO(n)みたいです。
  • 意外に言語の機能多い。

JDK8のjdepsは普通の業務開発でも便利

JDK8からjdeps(http://docs.oracle.com/javase/8/docs/technotes/tools/unix/jdeps.html)というJavaのクラス間の依存性を調べるツールが追加されました。

Java Day Tokyo2014ではこのツールを使ってJava9のProject Jigsawのための作業をしていると言っていたような気がしましたが…、そんなことより、このツールすごくSIer向けだと思いました。

使い方

使い方は簡単。あらかじめ依存性を調べたいソースをコンパイルしておいて

jdeps コンパイルしたクラスのディレクトリ
 または
jdeps JARファイル

とするだけ。(依存クラス・JARがJDK以外にいる場合は別途クラスパス指定可能)

そうすると

$> jdeps clojure
clojure -> /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar
   clojure (clojure)
      -> java.awt
      -> java.beans
      -> java.io
      -> java.lang
      -> java.lang.annotation
      -> java.lang.reflect
      -> java.math
      -> java.net
      -> java.sql
      -> java.text
      -> java.util
      -> java.util.concurrent
      -> java.util.regex
      -> javax.swing
      -> javax.swing.table
      -> javax.xml.parsers
      -> org.xml.sax
      -> sun.misc                                           JDK internal API (rt.jar)
   clojure.asm (clojure)
      -> java.io
      -> java.lang
      -> java.lang.reflect
   clojure.asm.commons (clojure)
      -> java.io
      -> java.lang
      -> java.lang.reflect
      -> java.security
      -> java.util
   clojure.core (clojure)
      -> java.lang
      -> java.util
      -> java.util.concurrent
      -> java.util.concurrent.atomic
(以下略)

(例としてClojure - https://github.com/clojure/clojure を使いました)

-vで、クラスごと出すことができます。

$> jdeps -v clojure
clojure -> /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar
   clojure.asm.AnnotationVisitor                      -> clojure.asm.Opcodes                                clojure
   clojure.asm.AnnotationVisitor                      -> java.lang.IllegalArgumentException
   clojure.asm.AnnotationVisitor                      -> java.lang.Object
   clojure.asm.AnnotationVisitor                      -> java.lang.String
   clojure.asm.AnnotationWriter                       -> clojure.asm.AnnotationVisitor                      clojure
   clojure.asm.AnnotationWriter                       -> clojure.asm.ByteVector                             clojure
   clojure.asm.AnnotationWriter                       -> clojure.asm.ClassWriter                            clojure
   clojure.asm.AnnotationWriter                       -> clojure.asm.Item                                   clojure
   clojure.asm.AnnotationWriter                       -> clojure.asm.Opcodes                                clojure
   clojure.asm.AnnotationWriter                       -> clojure.asm.Type                                   clojure
   clojure.asm.AnnotationWriter                       -> java.lang.Boolean
   clojure.asm.AnnotationWriter                       -> java.lang.Byte
   clojure.asm.AnnotationWriter                       -> java.lang.Character
   clojure.asm.AnnotationWriter                       -> java.lang.Object
   clojure.asm.AnnotationWriter                       -> java.lang.Short
   clojure.asm.AnnotationWriter                       -> java.lang.String
   clojure.asm.Attribute                              -> clojure.asm.ByteVector                             clojure
(以下略)

-pで指定のパッケージだけ出すということもできます。

$>  jdeps -v -p clojure.core clojure
   clojure.core$fn__6136$__GT_VecNode__6138           -> clojure.core.VecNode                               clojure
   clojure.core$fn__6145$__GT_ArrayChunk__6147        -> clojure.core.ArrayChunk                            clojure
   clojure.core$fn__6150$__GT_VecSeq__6158            -> clojure.core.VecSeq                                clojure
   clojure.core$fn__6165$__GT_Vec__6193               -> clojure.core.Vec                                   clojure
   clojure.core$reify__6203                           -> clojure.core.ArrayManager                          clojure
   clojure.core$reify__6206                           -> clojure.core.ArrayManager                          clojure
   clojure.core$reify__6209                           -> clojure.core.ArrayManager                          clojure
   clojure.core$reify__6212                           -> clojure.core.ArrayManager                          clojure
   clojure.core$reify__6215                           -> clojure.core.ArrayManager                          clojure
   clojure.core$reify__6218                           -> clojure.core.ArrayManager                          clojure
   clojure.core$reify__6221                           -> clojure.core.ArrayManager                          clojure
   clojure.core$reify__6224                           -> clojure.core.ArrayManager                          clojure
   clojure.core$vector_of                             -> clojure.core.ArrayManager                          clojure
   clojure.core$vector_of                             -> clojure.core.Vec                                   clojure

-e正規表現も使えます。

$> jdeps -v -e '.*\$.*' clojure
clojure -> /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar
   clojure.asm.commons.SerialVersionUIDAdder          -> clojure.asm.commons.SerialVersionUIDAdder$Item     clojure
   clojure.asm.commons.SerialVersionUIDAdder$Item     -> clojure.asm.commons.SerialVersionUIDAdder$Item     clojure
   clojure.core$_                                     -> clojure.core$_                                     clojure
   clojure.core$_DOT__DOT_                            -> clojure.core$_DOT__DOT_                            clojure
   clojure.core$_EQ_                                  -> clojure.core$_EQ_                                  clojure
   clojure.core$_EQ__EQ_                              -> clojure.core$_EQ__EQ_                              clojure
   clojure.core$_EQ__EQ___inliner                     -> clojure.core$_EQ__EQ___inliner                     clojure
   clojure.core$_EQ___inliner                         -> clojure.core$_EQ___inliner                         clojure
   clojure.core$_GT_                                  -> clojure.core$_GT_                                  clojure
   clojure.core$_GT_0_QMARK_                          -> clojure.core$_GT_0_QMARK_                          clojure
   clojure.core$_GT_1_QMARK_                          -> clojure.core$_GT_1_QMARK_                          clojure
   clojure.core$_GT__EQ_                              -> clojure.core$_GT__EQ_                              clojure
   clojure.core$_GT__EQ___inliner                     -> clojure.core$_GT__EQ___inliner                     clojure
   clojure.core$_GT___inliner                         -> clojure.core$_GT___inliner                         clojure
   clojure.core$_LT_                                  -> clojure.core$_LT_                                  clojure
   clojure.core$_LT__EQ_                              -> clojure.core$_LT__EQ_                              clojure
   clojure.core$_LT__EQ___inliner                     -> clojure.core$_LT__EQ___inliner                     clojure

-profileで、JREをどれだけ切り詰められるか、プロファイルをしりたい人にも親切。

jdeps -profile clojure
clojure -> /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar (Full JRE)
   clojure (clojure)
      -> java.awt                                           Full JRE
      -> java.beans                                         Full JRE
      -> java.io                                            compact1
      -> java.lang                                          compact1
      -> java.lang.annotation                               compact1
      -> java.lang.reflect                                  compact1
      -> java.math                                          compact1
      -> java.net                                           compact1
      -> java.sql                                           compact2
      -> java.text                                          compact1
      -> java.util                                          compact1
      -> java.util.concurrent                               compact1
      -> java.util.regex                                    compact1
      -> javax.swing                                        Full JRE
      -> javax.swing.table                                  Full JRE
      -> javax.xml.parsers                                  compact2
      -> org.xml.sax                                        compact2
      -> sun.misc                                           JDK internal API (rt.jar)

-dotoutput 出力フォルダ のオプションで、Graphviz形式で依存性の有向グラフも出すことができます。

$> jdeps -dotoutput dot clojure
$> ls dot
clojure.dot summary.dot
$> dot -Tgif dot/clojure.dot > clojure.gif

f:id:knjname:20140603010217g:plain

SIerやらとの親和性

「○○というクラスを修正したいけど、どこに影響が及ぶか知りたい。」というケースはよくあります。

まあEclipseでCtrl-Shift-G(シンボルの参照の検索)を連打してもいいのですが、やっぱめんどいですね。

SonarQubeとかでもクラスの依存性の解析は出たりしますが、ちょっとめんどいですね。

そういう人はJenkinsでコンパイルのついでにjdepsの出力結果を常に出しておくと便利です。私はそうしています。

複数プロジェクトがくんずほぐれつしている場合でも引数にクラスパスをちゃんと指定しておけばちゃんと複数プロジェクト間の依存性を見られます。

変なモジュールの絡み方してるやつも一発で見つけられますね。

みなさんもぜひ活用しましょう。

まとめ

  • jdepsコマンドを使えばクラス(パッケージ)間の依存性をチェックできる。
  • 業務開発で出がちなモジュール間の依存性チェックに使える。

並列で書き込めないデータ構造へのアプローチの考察(はしていない)

Collectors#toMap(): 00000215
Write to ConcurrentHashMap simultaneously: 00001567
Write to HashMap but write tasks are serialized: 00001787
Write to HashMap but write tasks are serialized (with parallel stream): 00001075

たとえばPOIのワークシートにマルチスレッドに書き込むと、もれなくぶっ壊れるわけですが、そういうマルチスレッド非対応のものに書き込む時はシングルスレッドなエグゼキュータにタスクをぶん投げると問題を解決できることが多いです。

特にオチ無し!

Java Day Tokyo 2014個人まとめ

今年もJava Day Tokyo 2014に参加してきました。

4月あたりにOracleのサイトで受講予約して、メールされた受講票のプリントと名刺を当日会場に持っていくだけ。参加無料です。

英語のセッションには同時通訳が付き、イヤホン付きトランシーバで聞けます。

参加したセッションは下記の通り。

  • 基調講演(K-1)
  • Java SE8概要(A-1)
  • Java SE8におけるHotSpotの進化(C-2)
  • Javaアプリケーション開発におけるテストとTDDの実践(C-3)
  • Lambda式とストリームAPI、並列処理の詳細(A-4)

個人的にメモってるものだけ、まとめていきます。

(よくわからなかったところはよくわからないままになっていますし、私の脳みそを通しているので不正確です。)

基調講演前

Java8を象徴するムービーが流れてました。

PermGenがなくなりますよーとかDate and Time APIがどーとかを読み取れるムービーでした。

基調講演

  • Java8出たよー。
  • CDC/CLDCは廃止したわー。
    • 8はCompact Profile 1/2/3/Full JREで頑張ってね。Full JREより前のプロファイルは画面無しの場合に最適だから。
    • Java9はもっとモジュール化すげえ頑張るから。
  • JDK8の日本語ドキュメント出たよー。
  • JavaGPU使うProject Smatra注目してねー。
  • IoTとか、NECPaPeRo petitとか、なんかアームがポーン動かしてるすごいチェスのデモとか

Java SE8概要

  • アノテーションがいろんなところでたくさん付けられるようになったよ。
  • もっと型推論できるようになったよ。
  • リフレクションでメソッドのパラメータ名にちゃんとアクセスできるようになったよ。 http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Parameter.html#getName--
  • APT(Annotation Processing Tool)消したわ。
  • Javadocについて、コマンドラインと同じ機能をjavax.toolsに追加したわ。あとDocLintツールも追加したわ。
  • 並列処理について
    • DoubleAccumlatorとか書き込み多し読み込み少なし効率がいいやつ追加したわ。
    • ForkJoinPoolもよくしたわ。
    • Fork-Join Framework使ったArrays#parallelSortも新規追加したわ。
  • Stream API追加したわ。遅延評価(遅延リスト)提供するわ。
  • java.util.function追加したわ。Predicate, Consumer, Function<T,R>, Supplier
  • Date and Time API追加したわ。区間とかつかえるよ。
  • JDBC4.2はじめました。Date and Time APIの型も使えるよ。
  • Base64コーデック、もうsunのパッケージの使わないでええで。
  • Unicode 6.2までサポート。(Java SE7は6.0まで。)
  • JEP124 https://blogs.oracle.com/mullan/entry/jep_124_enhance_the_certificate
  • これからはHTTPの仕様にそってパーミッション切れるようになった。
  • JavaFXアプリがライフサイクルメソッドで直接起動できるようになったよ。
  • Compact Profileが追加されたよ。1(10.4MB) < 2(16MB) < 3(30MB) < FullJRE(54MB)。
    • Jigsawへの第一歩だよ。いくつかのクラスローダへの前提とかプロプライエタリなSPIなコードをサービスローダ(java.util.ServiceLoader)のものにしたりJDKツールでコードの依存性を見たりとかしてる。
  • RhinoにかわるJavaScriptエンジンのNashorn、よろしくねー。ECMA Script5.1サポートだよ。jjsっていうコマンドラインツールJavaScript実行可能だよ。
  • 滅多に使わないGCの組み合わせを消したよ。
  • パーマネント(PermGen)領域消したよ。今まで格納していたインターン文字列、クラスメタデータ、static変数はヒープとネイティブメモリ(メタスペース)に入ります。HotSpot VMJRockitの統合によるものです。

Java SE8におけるHotSpotの進化

白人の方が出てきて、そのまま日本語しゃべりだしてびっくりした。

Buck David氏という元JRockitのエンジニアの方だそうです。 https://blogs.oracle.com/buck/

正直僕の脳にはむずかった。

まずはメモリ関係。

  • PermGenやめました
    • 正直PermGenのメリットがよーわからんかった
      • パーマネント領域どれぐらい取ればいいか試行錯誤
      • デフォルト小さい
      • GC中パーマネント領域もマークするオブジェクトを見るので、パフォーマンスに影響する。
  • メタスペース作ったよ
    • 今までの世代別GCの領域はそのまんま。クラスメタデータはメタスペースで管理。
    • インターン文字列はヒープ。非オブジェクトの一部がメタスペースに行ったよ。
    • 別にパーマネント領域の名前が変わっただけじゃないよ。
      • パーマネント領域はランタイム時にサイズを変更できなかったよね。
      • メタスペースはヒープとかと関係ないし、分散・拡張・縮小できる。
    • デフォルトはサイズ制限無しで増えるよ。
    • GCに見られないからGCに悪影響を及ぼさないよ。
    • アンロードはClassLoaderの単位で行うから断片化しづらいよ。(メモリ確保はOSのページ単位)
  • Javaのヒープからメタスペース中のクラスのメタデータにポインタを持つよ
  • ClassLoaderごとに自分のメタスペースを持ち、メタスペースは複数のメタチャンクを持ち、VirtualSpaceが複数のメタチャンクを含むよ (メタスペースもVirtualSpaceもリストかな)
  • メタスペースがHWM(High water mark - -XX:Min(Max)MetaspaceFreeRatio)を超えるとFull GC起こるよ。
    • Full GCおこらないとMetaspaceのコレクションが行われないよ
  • クラスローダが消えないかぎりそいつが持ってるメタスペースは確保され続けるよ。
  • Compressed Oops(圧縮参照)っていう64bitアドレスを32bitにして持つ仕組みがあるよ。
    • ヒープからメタスペースへのポインタも圧縮されるよ。
    • Compressed Class Pointer Space(CCPS)っていう領域を作ってヒープからのメタスペースへの圧縮参照の中継地点を作ったよ。
  • メモリオプション追加になったよ
    • -XX:MaxMetaspaceSize (default:unlimited) あんまり使うのは非推奨。
    • -XX:MetaspaceSize (default:21MB) 初期メタスペースサイズ。拡張のオーバヘッドや起動時Full GCを防ぎたい人向け。
    • -XX:MinMetaspaceFreeRatio / -XX:MaxMetaspaceRatio デフォルト 40/70。メタスペースのHWM制御用。
    • -MM:+UseCompressedClassPointers 64bitのポインタを32bitに縮めるやつ。64bit環境ではデフォルトで有効。
    • -XX:CompressedClassSpaceSize (default:1GB) CCPSの大きさ。この分取るんじゃなくて、必要に応じてコミットしていく。
  • MemoryManagerMXBean: MetaspaceManager のCompressedClassSpaceの数値はメタスペースの領域込みの表示だから注意な。
  • jmap -permstatはjmap -clstatsに
  • jstat -gc はメタスペースみます
  • jcmd PID GC.class_statsでメタデータだしたけりゃJVMを -XX:+UnlockDiagnosticVMOptions付けて起動しな http://www.slideshare.net/TakahiroYamada3/tools-for-metaspace

Tiered Compilation(実行時のクラスコンパイル最適化の一種)について

フォールスシェアリングの回避(JEP142)。Fork Join で競合するフィールドが同じキャシュラインに入るようにオブジェクトのレイアウトを頑張るとかなんとか

Indy(Invoke Dynamic)の高速化とNashornで従来のx2 - x4ぐらい性能出るよ。

あとJVMレベルで直接クラスライブラリのAPI実装を提供しいたり、CPUのAES専用命令を使ったり、JEP 171/164とか、JDK9でまた使わないGCの組み合わせ消しまーすとか。JDK8で何もしなくても50%パフォーマンス向上したよー!とか。

総じて濃かった。

Javaアプリケーション開発におけるテストとTDDの実践

ユニットテスト

  • 自動テストは早めに行うと繰り返しが多くなるから収穫が大きい
  • 自動テストはモック派とリアルDB使う派に分かれるね
  • リアルDB使う場合は後処理じゃなくて、テストの初期化でDBの初期化とかやったほうが楽だしコケても原因がわかりやすいよ。
  • DbUnit用のアノテーションとか作ったよ。
  • テストしづれえシステム時間とかそういうのは無視ってもいいんじゃない。こだわっても実利ない。

総合テスト

http://d.hatena.ne.jp/nekop/20140523/1400810907

t_wada

英語と違い、日本語でまくしたてられると予備校の授業聞いてるみたいでした。

Lambda式とストリームAPI、並列処理の詳細

  • JDK8でλはじまるしStream APIはじまるわ。
  • @FunctionalInterface/Predicate/Function/Consumer/Supplier/#skip(n)/#limit(n)/#sorted()/#distinct()/Stream#stream()/Arrays#asStream()/IntStream/reader.lines()/.collect(Collectors.toList() / joining("")/toSet/averaging/toMap/partitioningBy/groupingBy)/peek()/findFirst : Optional/parallel()/reduce() (reduction)
  • Stream: a multiplicity of values
  • ストリームAPIでは不変値を扱うのがコツ。これで並列もへっちゃら。

正直眠かった。すまんかった。

総括

去年につづき、Java8の話題が中心でした。今のスケジュールだとJava7が来年3月にアップデート提供終了になっちゃうので、これからの開発はJava8を使う人も多いのではないでしょうか。

英語について、だいたいスピーカーの英語はそれなりの人なら割と聞き取れると思いました。でも恥ずかしがらずにイヤホンつけちゃっていいと思います。

会場について。電源とかは、期待してません。あの来場者では十分に供給するのは無理だろうし、個々人で対策するほうが理にかなっている気がします。回線は、自分のWiMAXすらつながらないこともありました。人多すぎ?

廊下が狭かったです。会場移動が間に合わず、おしっこかなり我慢しました。

グッズコーナーが今年もあり、ソーラー電灯買ったんですが、充電してもうまくつかない(´・ω・`)。書籍コーナーはO’Reillyがお買い得でした。英語書籍\1,500!

また来年も縁(?)があればいきたいと思います。

docker buildのUploading context...が長い場合

何故かdocker buildした時にUploading contextが長時間流れる状態に突入する人は…

#> docker build -t mycentos

Uploading context... 200MB

(↑たぶんこんな感じの表示)

docker buildしてるディレクトリにイメージビルド資材以外を置くのをやめましょう。

What is the meaning of "Uploading context" when executing ``docker build``? · Issue #2342 · dotcloud/docker · GitHub

たとえば、カレントディレクトリにあるDockerfileをベースにdocker buildをする時、Uploading context...が何をしているかといえば、そのディレクトリにある資材をtar化してデーモンに送付しています。

(Dockerfileの存在しているディレクトリ以下を送るらしい)

ADDしてるものだけ送るんじゃないのって思うけど違うんですね~!

これやらかしても、だいたいはビルドが遅くなる以外実害が無いですが、ただでさえ重いイメージ作成が激ストレスになるので、気をつけたほうがいいです。

っていうかマニュアルのどっかにこういうこと書いてあるっけ?

私はマヌケなのでひっかかっていましたが、皆さんが引っかからぬよう願っております。

開発サーバ構築しよう~LDAP(認証基盤)を立てよう~開発サービスはDockerで立てよう

LDAPサーバ立てよう

開発プロジェクト用の開発サーバ構築の一貫として

  • SCM(Subbversion, Git)を立てるにしろ
  • ITS(Redmine, Trac)を立てるにしろ
  • CIサーバ(Jenkins)を立てるにしろ
  • モジュールリポジトリサーバ(Nexus)を立てるにしろ
  • Wiki(MediaWiki)を立てるにしろ
  • コードインスペクション用サーバ(SonarQube)を立てるにしろ
  • メールサーバ(Dovecot+Postfix)を立てるにしろ
  • 他もろもろを立てるにしろ

大概そいつらにはメンバーごとのログインという共通要件があります。

メンバーが追加されるたびに、手順書を更新していちいち各サービスのユーザ登録手順を増やすのもいいんですが、そういうことやってると、

  • 登録・削除が面倒だったり、
  • アカウントIDやパスワードがサービスごとブレはじめたり、

大変不便なので、Open LDAPとかで共通のLDAP認証サーバを立てましょう。

LDAPサーバにユーザのアカウント情報や集約され、各サービスがそれを参照しにいくので、全サービスでログイン情報を一元化できます。

また、グループ情報も集約でき、Jenkinsでグループ情報を見て権限を分類したり、メールサーバがメーリングリスト代わりにしたりできます。

本当に手間が浮くか

と言われると、正直のところ微妙です。

RedmineやJenkins、MediaWikiなどよくあるLDAPログインの実装だと、LDAPでログイン成功した瞬間に各サービスにアカウントをオンザフライで作成してくれたりします。

その時アカウントに使うメールアドレスや姓・名、ユーザIDはLDAPから自動補完してくれます。

逆に言うと、LDAPで補えない情報は、そのオンザフライアカウント作成後で手作業でつけろというわけです。

各サービスを拡張して全てLDAPから必要なユーザ情報を拾えるようにするのも大変ですし、どうにかならないかなあとも思いますが、やっぱりLDAPは無いよりあったほうが楽に感じます。

社内のActiveDirectoryとかは使わない

プロジェクトごとにLDAP立てないで、会社で使っているActiveDirectoryにLDAP喋らせてつなげばいいじゃないか的なことを考えるかもしれませんが(あるいはActiveDirectoryと直接つなげるとか)、下記のような例外に対処しづらいと思います。

  • 社外の人
  • 一時的な人
  • マシン用アカウント(私はJenkinsやRedmine用の非人間用のアカウント作成したりします)

プロジェクトと社内メンバーは厳密には違うものです。たとえ社内メンバーがプロジェクトグループに収まっていても、です。

そこのギャップをちゃんと吸収できればアテにしてもいいですが、大概、プロジェクトのコントロールから外れる資産をアテにすると割りとロクでもない結果に繋がりそうだと僕は思うので、自前でLDAP一式そろえることをおすすめします。

増えまくる開発用サービスはDockerで対処しよう

また上記のような開発にまつわるサービスはガンガン増えていくので、Ubuntuなりの上でDockerを使ってコンテナ型で起動することをおすすめします。

Dockerをつかわず、あるOS上に直接いろんなサービスを入れると整理がつかなくなりますし、マシンをわけまくっても費用が高く付きます。

Dockerの上に立てておけば、万が一性能問題とかである開発サービスだけ別サーバに疎開させることになったとしても、簡単に移動・複製ができます。

(僕の場合はnginxでDocker上のHTTPしゃべるサービスにリバースプロキシさせています。)

Dockerのサーバ自体も仮想環境で動かそう

これに加え、Dockerが動くサーバOS自体もKVMといったハイパーバイザー上で仮想ゲストOSとして動かすと小回りが効いて便利です。

(別にKVMじゃなくてもいいです。ESXiなりHyper-Vなりお好きなのをどうぞ。)

仮想環境でOSを動かしておくと、ゲストOSのバックアップ含め、いろんな操作がラクチンになるのでおすすめです。

個人的に思うこと

こういう内部向けの開発サービスの構築って大事なもののはずなのに、非技術系の人が集まると異様に軽視されはじたりします。

ま、そういう人たちには、こういうモノがどういう価値を持つかなんて分からないし興味が無いので、当然といえば当然ですが、例えば最終的に発生したプロジェクト炎上という現象の原因が、結局はプロジェクトマネジメントとか顧客とか宇宙の法則とか、そういった言葉でしか締めくくれないとしたら、こういった要素をあまりにも軽んじているのではないかと思います。っていうかあんたら開発の仕事してんだよね?!

残念ながらそういった人たちが集まる場は多いですが、そういった中でも慎重かつ大胆に構築して、触らせてその価値を体感してもらうことが大事です。

構築ごとにわざわざ承認とったりすると、たぶん認めてもらえないので動く現物ぶつけたほうがいいと思います。

でも、本当のことを言ったら、組織として開発の成果を出すのに一番大事なのは、こういったことを軽視しない風土でしょうね。

少なくとも開発者が片手間にイヤイヤやらされるようなことじゃないと思うのです。

Jenkinsのビルドパイプライン系plugin3種比較

Jenkinsのビルドパイプライン系plugin3種類を見ていきます。

ちなみにこれらプラグインはここから適当に探しまんた。

https://wiki.jenkins-ci.org/display/JENKINS/Plugins

ちなみに、ビルドパイプラインって何?

例えば、あるソフトウェアをデリバリーする際、

  1. プロダクションコードをビルドして、
  2. 実行環境に送り込んで、
  3. その上でアプリケーションを再起動して、
  4. テストする

とか一般的だと思うのですが、その一連のジョブ動作をビルドパイプラインといいます。(たぶん)

ここではある特定目的をもった一連のジョブ連鎖の塊と認識してもらってOKです。

特定単一のルートジョブから複数の子孫ジョブが連鎖して起動して1つのビルドパイプラインを形成するのが典型的です。

ちなみに私は30-40個ぐらいジョブがくっついているビルドパイプラインを作ったことがあります。社会の要請に応えた結果だからしょうがないね。

※ビルドジョブは基本バラしましょう。バラして不都合があったらくっつけましょう。

今回使うビルドパイプラインのサンプルジョブについて説明

今回は以下の4種類のジョブを説明のために定義しています。

  • A
    • B (depends on A)
    • C (depends on A)
      • D (depends on A and B)

タイトルどおり、BとCはAの終了後同時に起動し、Dはその両方が終わった時に起動するという設定です。

BとCが両方終わった時のDの起動については Join Plugin - Jenkins - Jenkins Wiki を使ったり使ってなかったりします。

Build Pipeline Plugin

https://wiki.jenkins-ci.org/display/JENKINS/Build+Pipeline+Plugin

f:id:knjname:20140506191038p:plain

オーソドックスなビルドパイプラインプラグイン。雑誌などでJenkinsの紹介記事を見る人がおそらくいちばん目にしてそうなプラグイン

基本的に各ジョブが持っている下流ビルド(このビルド後に次のジョブを起動する)、上流ビルドを表示するだけのプラグインです。

使用時には下流ジョブをたどるためのルートジョブを一つ指定して使います。

ただのビューなので、特にビルドの能力や可能性を上げるわけではありません。どのビルドの後に何が起動するのか常に表示されて分かりやすいだけです。

大概の用途にピッタリだと思います。

ただジョブ一つ一つに上流下流設定するのは面倒ですね。クリックゲーになりがち。

Build Flow Plugin

https://wiki.jenkins-ci.org/display/JENKINS/Build+Flow+Plugin

f:id:knjname:20140506193902p:plain

Build Pipeline Pluginの自由度の低さに不満を持った人用プラグイン

あちらとは違い、Build Flow Plugin専用ジョブを作成し、そこにGroovyによるDSLでビルドのフローを定義します。

Build Flow Pluginと違って、ビルドの順番や条件がいろんなジョブに分散してとっちらからず*1、便利なDSLで思ったようなパイプライン作成ができたりします。

しかしこの動的な自由度の代償に欠点がいくつかあります。

  • 実際にビルドパイプラインを動かすまで、何がどう連携して動くかは見ることはできません。(まあDSLの機能削れば予測可能なビルドフローDSLっていうのも論理的には作れますけどね。)
  • パイプラインの途中からやり直すことができない。稀によくパイプラインの途中で失敗する場合、時間をおいてパイプラインの途中からジョブをやり直したいことがあります。普通の上流・下流ビルドの起動構成だとこの類のリトライ操作は可能ですが、このプラグインでビルドフローを定義している場合は最初からのやり直ししかできません。
  • ジョブをリネームしても追随しない。

ビルドパイプラインが動いた後のジョブフローは Build Graph View Plugin で見ることが可能です。

f:id:knjname:20140506194010p:plain

ちなみにフロー上の起動ジョブ内で独自に下流ジョブの起動を持っていると、Build Flow Pluginのジョブ起動とは別にそこで新規下流ジョブの起動が始まってしまうので、このプラグインで扱うジョブについては一切下流ジョブの起動設定を持たせないほうがいいでしょう。(プラグインが配慮してジョブの起動をとめてくれたりは一切しません。)

下記画像は試しにBuild Flow Pluginとは別にジョブA内でジョブDを下流ビルドとして起動したみた画像です。きっちり起動しちゃってるのが分かります。

f:id:knjname:20140506194357p:plain

Delivery Pipeline Plugin

https://wiki.jenkins-ci.org/display/JENKINS/Delivery+Pipeline+Plugin

後発のプラグイン。基本的には Build Pipeline Pluginと発想は似ています。

ジョブフローはジョブごとに設定をして、それをどう表示するかだけしか扱わないビュー型のプラグインです。

ジョブごとにジョブのカテゴリ(Stage Name)、ジョブのタスク名(Task Name)を設定し、そのカテゴリごとにジョブを整理して表示してくれます。

百聞は一見にしかず、画像を見たほうがはやいでしょう。

まず、このプラグインをインストールすると毎回ジョブの設定画面の上部に Delivery Pipeline configurationと出てきてウザいことこの上ないですが、各ジョブごとStage Name、Task Nameを設定します。(画像上のジョブの並びが滅茶苦茶ですが気にしないでください)

f:id:knjname:20140506201311p:plain

次に新規ビュー作成でDelivery Pipeline Viewを選んでビューを作成し、PipelinesのComponentsに適当にInitial Jobを選んで追加してあげると、次のように表示されます。

f:id:knjname:20140506201643p:plain

先ほど設定したカテゴリ(Stage Name)ごとにジョブが並んでおり、そのジョブの名前もタスク名(Task Name)になっているのがわかるでしょうか?

非常に説明的でわかりやすいですね。

また、いろんなビルドパイプラインを1ページに収めることができます。

私みたいにパイプラインに大量にジョブを抱えている人はBuild Pipeline Pluginよりこっちのほうが見やすくていいかもしれません。

まとめ

  • 普通の用途にはBuild Pipeline PluginかDelivery Pipeline Pluginが最適でしょう。
  • どうしてもそれじゃやってられない人はデメリットをのんで、Build Flow Pluginを使いましょう。

*1:ただ、ビルドフローがとっちらかないといっても上流ビルド成果物のコピーとかは個々ジョブ内で定義するしかありません。