1. 目的

この記事では、Scalaでの暗黙的なインポートについて説明します。 それらが何であるかをカバーしてから、Predefパッケージについて少し詳しく説明します。

2. 暗黙のインポート

暗黙的なインポートとは、プログラマーが明示的にインポートすることなく、すべてのコンパイルユニットまたはScalaプログラムデフォルトで使用できるインポートです。 暗黙的にインポートされるパッケージはごくわずかです。

import java.lang._
import scala._
import Predef._

これらについては、この記事の後半で個別に説明します。

3. :importコマンド

Scalaコンソールで:importコマンドを実行することで、すべての暗黙的なインポートを簡単に確認できます。

scala> :import
1) import java.lang._ (136 types, 144 terms)
2) import scala._ (179 types, 167 terms)
3) import scala.Predef._ (100 terms, 58 are implicit)

コンソールで:import を実行すると、3つのパッケージすべてがデフォルトでインポートされていることがわかります。

4. パッケージjava.lang

Javaプログラマーは、java.langが常にデフォルトでJavaプログラムにインポートされることを知っているでしょう。 これは、JavaにおけるScalaのルーツのアーティファクトです。 このパッケージは、ObjectClassのように、Java基本的なクラスを提供します。 また、すべてのプリミティブ型のラッパークラスも含まれています。

Math クラスは、平方根や対数などの一般的に使用される数学関数のメソッドを提供しますが、StringおよびStringBuilderクラスは、文字列に対する一般的な操作を提供します。

ClassLoader Process SecurityManager などのクラスは、ホスト環境の動的なロードと管理を可能にするシステム操作を提供します。

また、 Throwable クラスには、例外としてスローされる可能性のあるオブジェクトが含まれています。

5. パッケージscala

Package scala._が次のインポートです。 Javaとは異なり、java.langパッケージからの先行するインポートをオーバーライドするという点で独特です。 たとえば、scala.StringBuilderjava.lang.StringBuilderをオーバーライドします。 Scalaパッケージの詳細については、ScalaDocをご覧ください。

6. パッケージPredef

Predef オブジェクトには、タイプ、タイプ間の暗黙的な変換、およびユーティリティメソッドが含まれています。 このインポートにより、scala.Predefオブジェクトがプログラムスコープに自動的に配置されます。 オブジェクトのすべてのメンバーがプログラムで使用できます。

このパッケージの主要部分を見てみましょう。

6.1. ユーティリティメソッド

Predef は、6つのユーティリティメソッドを提供します。 それらを見てみましょう。

??? は、まだ実装されていないメソッドを示すために使用されます。 そのようなものを呼び出すと、 NotImplementedError。 それがどのように機能するか見てみましょう:

def notImplemeted:Int => Boolean = ???
assertThrows[NotImplementedError] {
  notImplemeted
}

ここでは、メソッドを割り当てました notImplemented to ??? そしてそれを呼ぶことは上がるだろうと主張した NotImplementedError

classOf メソッドは、クラスタイプのランタイム表現を取得します。 classOf [T] は、JavaのクラスリテラルT.classと同等です。 classOf Stringタイプが何であるかを主張しましょう。

assert("class java.lang.String" == classOf[String].toString)

Identity メソッドは、入力値と同じタイプの同じ値を返します。

assert("some" == identity("some"))

String入力「some」を使用してidentityを呼び出すと、同じ値とタイプが返されます。

implicitly メソッドは、タイプTの暗黙的な値を返します。

implicit val a = "test"
assert("test" == implicitly[String])

“ test”の値でStringタイプを宣言しました。 暗黙的にStringタイプで呼び出すと、元の宣言された値が返されます。

locally メソッドは、Identityのほぼ別の名前です。 これは、コードブロックを匿名クラスとしてではなく、式としてマークするために使用されます。 これの主なアイデアは、ぶら下がっているローカルコードブロックを回避することです。 それがどのように機能するか見てみましょう:

object Local {
  object A
  {
    val a = 10
  }

  object B

  locally {
   val b = 20
  }
}

locally でない場合、上記はコンパイルされず、オブジェクトBの後の新しい行はコードブロックを分離します。

最後に、 valueOf メソッドは、一意の住民を持つタイプの単一の値を取得します。

assert(25 == valueOf[25])

6.2. アサーション

これらは、プログラムの検証と実行時の正確さをサポートするメソッドです。

assert メソッドはブールステートメントをテストし、falseの場合はAssertionErrorをスローします。 みてみましょう:

assertThrows[AssertionError] {
  assert(2 + 2 == 5, "sum doesn't add up")
}

assertThrows[AssertionError] {
  assert(2 + 2 == 5)
}

assert メソッドは式をテストし、falseの場合はAssertionErrorをスローします。 この方法はassertに似ていますが、表現される意図が変更されています— assert には証明が必要な述語が含まれ、assertには静的な公理が含まれますチェッカー。 それがどのように機能するか見てみましょう:

assertThrows[AssertionError] {
  assume(2 + 2 == 5, "sum doesn't add up")
}

assertThrows[AssertionError] {
  assume(2 + 2 == 5)
}

最後に、 require は式をテストし、falseの場合はIllegalArgumentExceptionをスローします。 このメソッドはassertに似ていますが、条件に違反するメソッドの呼び出し元に責任を負わせます。

assertThrows[IllegalArgumentException] {
  require(2 + 2 == 5, "sum doesn't add up")
}

assertThrows[IllegalArgumentException] {
  require(2 + 2 == 5)
}

6.3. エイリアス

Predef には、インポートなしで不変の型をスコープに取り込むいくつかのエイリアスがあります。 これらは、クラス関数マップセット、および文字列です。 これらは、デフォルトの可変Java MapおよびSetクラスをインポートするリスクを回避することにより、不変型の使用を容易にします。

type Class[T] = java.lang.Class[T]
type Function[-A, +B] = (A) => B
type Map[K, +V] = collection.immutable.Map[K, V]
type Set[A] = collection.immutable.Set[A]
type String = java.lang.String 
val ->: Tuple2.type 
val Map: collection.immutable.Map.type
val Set: collection.immutable.Set.type

6.4. 文字列変換

文字列変換により、からの変換が簡単になります Scalaに StringOps また WrappedString。 これらの輸入品のため、私たちはどんなものでも扱うことができます java.lang.String オブジェクトとして StringOps また WrappedString 、治療するより強力な方法を使用できるようにしますコレクションとして:

implicit def augmentString(x: String): StringOps
implicit def wrapString(s: String): WrappedString

このことを考慮:

assert("ello" == "hello".filter(_ != 'h'))

デフォルトのjava.lang.Stringにはfilterメソッドがないことがわかっています。 StringOps objectに暗黙的に変換されるため、ここで使用できます。

6.5. 暗黙のクラス

Anyタイプにはいくつかの便利な拡張機能もあります。

implicit final class ArrowAssoc[A] extends AnyVal
implicit final class Ensuring[A] extends AnyVal
implicit final class StringFormat[A] extends AnyVal

保証がどのように機能するかの例を見てみましょう。

def doubleInput(n:Int) Int = {
  n * 3
} ensuring(n => n % 2 == 0)

assertThrows[AssertionError] {
  doubleInput(3)
}

上記の例では、結果が2で割り切れないため、doubleInputを呼び出すとAssertionErrorがスローされます。

6.6. CharSequenceラッパー

暗黙のCharSequenceクラスを実装するラッパーがいくつかあります。

final class ArrayCharSequence extends CharSequence
final class SeqCharSequence extends CharSequence
def ArrayCharSequence(arrayOfChars: Array[Char]): ArrayCharSequence
def SeqCharSequence(sequenceOfChars: collection.IndexedSeq[Char]): SeqCharSequence

6.7. JavaからScalaへのコンバーター

これらは、すべてのJavaプリミティブから同等のScalaへの暗黙のコンバーターです。 Javaプリミティブ型ごとに1つのメソッドがあります– boolean、byte、char、double、float、int、long 、およびshort

例を見てみましょう:

assert(false.compare(true) == -1)

プリミティブbooleanにはcompareメソッドがないことがわかっています。 ブール値への暗黙の変換のために使用可能です。

6.8. ScalaからJavaへのコンバーター

これらのメソッドは、前のセクションのメソッドの逆です— Scala AnyValから同等のJavaプリミティブラッパーへのコンバーター。 繰り返しますが、Javaプリミティブ型ごとに1つあります。

6.9. ArrayからArraySeqへのコンバーター

パターンに従って、これらはJava ArrayからScalaArraySeq への変換を、プリミティブ型ごとに1つずつ実行するメソッドです。

assert(Array(1,2,3).reverse.last == 1)

ここでは、 Array[Int]からArraySeq[Integer] に暗黙的に変換されるため、メソッドreverseを呼び出します。

6.10. その他のリッチコンバーター

最後に、Java型を同等の豊富なScalaデータ型に変換するメソッドがあります。 リッチタイプは基本タイプの動作を拡張するため、冗長なボイラープレートコードが不要になります。 たとえば、 reverse a Stringを実行できます。

assert(“ xyz” .reverse ==“ yzx”)

Stringタイプのreverseメソッドはありませんが、から RichString への暗黙の変換により、可能です。

7. 結論

この記事では、Scalaの暗黙的なインポートについて説明し、Predefオブジェクトについて詳しく調べました。

いつものように、すべてのコード例はGitHubにあります。