Kotlinの後味、パート2

最後の部分では、コトリンのサンゴ礁について話しましたが、これでは、それらが重要であることを伝えます。



何度か尋ねられましたが、KotlinでJavaからの移行を促進できるものは何ですか、その機能は何ですか? はい、KotlinはNull-safetyと多くの構文糖を導入し、Javaの弱点の一部をカバーしました。 しかし、これは移行の理由にはなりません。 何になりますか? 新しい機会と新しい哲学。







新機能



1.ヌルの安全性



これはそもそも述べられています。 それは本当にコードをより安全にします。 はい、Javaを呼び出すときに問題があります。 要約すると、次の2つのオプションになります。



  1. サードパーティライブラリの呼び出し。 これは、結果が割り当てられる変数の型の明示的な宣言によって、または既に呼び出しのチェーンをまっすぐにしたい拡張機能を記述することによって処理されます(例は最後にあります)。
  2. 外部からのメッセージの受信(REST、MQなど)。 ここでSpring Validateが助けになります。


2.コルーチン



私はまだ自分で使用していませんが、明らかに、これはマルチスレッドプログラミングへのアプローチを大きく変える可能性があります。 UPD コルーチンに関する記事を修正して書い



3. JavaScriptでのコンパイル



残念ながら、私はまだ試していません。 私のプロジェクトの最初の段階では、ベータ版でしかなかったので、アンギュラーに詳しくありませんでした。 試行錯誤の手-サーバーとクライアント用のDTOを備えた単一モジュール



新しい哲学



Kotlinの哲学は、 簡潔安全相互運用可能ツールフレンドリーです (公式Webサイトおよびレポートから)。



1. 相互運用可能



Javaの互換性は、完全に100%の往復です。 私が何をしても、すべてが完璧に機能しました。



2. ツールに優しい



Eclipseで試したことはありませんが、Intellijのすべてが素晴らしく、改善を続けています。



3. 安全



私の実際的な見方では、これが最も重要なことです。 簡潔で、相互運用可能で、ツールに優しいのは、JVMで言語が生き残るための最小条件です。そうしないと、Javaが失われます。



ヌル安全+可変性



たとえば、ローカル変数は文字列のリストです。 Javaでは、コンパイラは2つのオプションのみを認識しています。



List<String> list1; final List<String> list2;
      
      





2番目のオプションは、孤立したケースにあります。 通常、これは8番目のJavaではなく、このリストは匿名クラスで必要です。



そして、コトリンは次のとおりです。



 val list1: List<String>? val list2: List<String?>? val list3: List<String> val list4: List<String?> val list5: MutableList<String>? val list6: MutableList<String?>? val list7: MutableList<String> val list8: MutableList<String?> var list9: List<String>? var list10: List<String?>? var list11: List<String> var list12: List<String?> var list13: MutableList<String>? var list14: MutableList<String?>? var list15: MutableList<String> var list16: MutableList<String?>
      
      





それは何を与えますか? 各タイプには、独自の保証と、許可されている一連の操作があります。 したがって、+ = nullはvar list12:List <String?>でのみ呼び出すことができ、val list8:MutableList <String?>およびvar list16:MutableList <String?>に(null)を追加します。



各宣言では、完全な型を書くことは採算が取れません。 したがって、型推論があります。



 val test = Random().nextBoolean() val list1 = if (test) null else listOf("") val list2 = if (test) null else listOf(null, "") val list3 = listOf("") val list4 = listOf(null, "") val list5 = if (test) null else mutableListOf("") val list6 = if (test) null else mutableListOf(null, "") val list7 = mutableListOf("") val list8 = mutableListOf(null, "") var list9 = list2?.filterNotNull() var list10 = list2 var list11 = list2?.filterNotNull() ?: emptyList() var list12 = list2 ?: emptyList() var list13 = list2?.filterNotNull()?.toMutableList() var list14 = list2?.toMutableList() var list15 = list2?.filterNotNull()?.toMutableList() ?: mutableListOf() var list16 = list2?.toMutableList() ?: mutableListOf()
      
      





コードを記述するとき、後で記述しないように、余分なフィールドをNULL可能として宣言したくないですか? and ?:そして、ほとんどの操作は不変のコレクションをもたらします。 その結果、最も狭い状態がコード内で宣言されるため、より厳密なコントラクトが提供され、プログラムの複雑さが軽減されます。



他の言語機能による安全なサポート



  1. 当然、パラメーターのないコンストラクターはオブジェクトの未コンパイル状態(たとえば、nullユーザーログインなど)を作成するため、パラメーターとフィールドのセッターなしでコンストラクターを残します。
  2. この言語は、ローカル変数が存在しないことを奨励しています-中間状態はありません。
  3. 安価なDTO データクラス 。 そのため、モデルのコントラクトを弱めることなく、オブジェクトをUIに転送するときに状態の制御を弱めることができます。
  4. メソッドのオーバーロードのコストを削減- デフォルトのパラメーター -セッターの束を記述する誘惑はありません。



     data class Schedule( val delay: Int, val delayTimeUnit: TimeUnit = TimeUnit.SECONDS, val rate: Int? = null, val rateTimeUnit: TimeUnit = TimeUnit.SECONDS, val run: () -> Unit ) fun usage() { Schedule(1) { println("Delay for second") } Schedule(100, TimeUnit.MILLISECONDS) { println("Delay for 100 milliseconds") } Schedule(1, rate = 1) { println("Delay for second, repeat every second") } }
          
          







表示されるもの



  1. インラインクラス/値クラス。 このクラスなしでコンパイルしながら、プリミティブのラッパークラスを作成できます。 たとえば、ログインと電子メールの2種類の行を作成できます。これらは互いにキャストされません。 Jpointでそれについて聞いた。
  2. 本当に不変のデータ。 オブジェクトの可変性の構文サポート。 不変オブジェクトには、不変オブジェクトへの参照を含めることはできず、変更することはできません。 言語の新機能に対する投票で3位になりました。


4. 簡潔 (私のプロジェクトの例、ほぼそのまま)



「1つのファイル-1つのクラス」という制限はありません。



私にとって、春のデータの操作は次のようになります(すべて1つのファイルに含まれています)。
 @Repository interface PayerRepository : CrudRepository<Payer, Int> { fun findByApprenticeId(id: Int): List<Payer> } @Repository interface AttendanceRepository : CrudRepository<LessonAttendance, LessonAttendance.ID> { fun findByDateBetween(from: Date, to: Date): List<LessonAttendance> } fun AttendanceRepository.byMonth(month: Date): List<LessonAttendance> { val from = month.truncateToMonth() val to = month.addMonths(1).subtractDays(1) return findByDateBetween(from, to) } // 10  inline fun <reified T, ID: Serializable> CrudRepository<T, ID>.find(id: ID): T { return findOne(id) ?: throw ObjectNotFound(id, T::class.qualifiedName) }
      
      







拡張機能



DateUtilsへのアピールを修正します
コトリン



 fun isJournalBlocked(date: Date, forMonth: Date) = forMonth <= date.subtractMonths(1).subtractDays(10) //   20  fun Date.subtractMonths(amount: Int): Date = DateUtils.addMonths(this, -amount) //   8  fun Date.subtractDays(amount: Int): Date = DateUtils.addDays(this, -amount)
      
      





Java



 public boolean isJournalBlocked(Date date, Date forMonth) { return date.compareTo(DateUtils.addDays(DateUtils.addMonths(forMonth, -1), -1)) <= 0; }
      
      







いくつかのパラメーターの変更のシーケンスを保存する必要がありました。このため、Historyインターフェースを作成して、そのようなパラメーターと拡張SortedMap <Date、out History>を保存し、変更後のコンテンツを調整しました。



実装
 interface History<out T> { val begin: Date var end: Date? fun historyOf(): T fun containsMonth(date: Date): Boolean { val month = date.truncateToMonth() return begin <= month && (end == null || month < end) } } fun <T> SortedMap<Date, out History<T>>.fix() { removeRepeatedNeighbors() val navigableMap = TreeMap<Date, History<T>>(this) values.forEach { it.end = navigableMap.higherEntry(it.begin)?.value?.begin } } private fun <T> SortedMap<Date, out History<T>>.removeRepeatedNeighbors() { var previousHistory: T? = null for (history in values.toList()) { if (history.historyOf() == previousHistory) { remove(history.begin) } else { previousHistory = history.historyOf() } } } //usage: fun setGroup(from: Date, group: ClassGroup) { val history = GroupHistory( this, group, from.truncateToMonth(), null ) groupsHistory[history.begin] = history groupsHistory.fix() this.group = groupsHistory.getValue(groupsHistory.lastKey()).group }
      
      







収集操作



例1
コトリン



 val apprentices: List<ApprenticeDTO> = apprenticeRepository.findAll() .map(::ApprenticeDTO) .sortedWith(compareBy({ it.lastName }, { it.firstName }))
      
      





Java



 List<ApprenticeDTO> apprentices = StreamSupport.stream( apprenticeRepository.findAll().spliterator(), false ).map(ApprenticeDTO::new) .sorted(Comparator.comparing(ApprenticeDTO::getLastName) .thenComparing(Comparator.comparing(ApprenticeDTO::getFirstName))) .collect(Collectors.toList());
      
      







例2
コトリン



 val attendances: Map<Pair<Date, Int>, Int> attendances = attendanceRepository .byMonth(month) .groupBy { it.date to it.group.id } .mapValues { it.value.count() } .toMap()
      
      





Java



 Map<Pair<Date, Integer>, Integer> attendances = attendanceRepository .byMonth(month) .stream() .collect(Collectors.groupingBy((it) -> new Pair<>(it.getDate(), it.getGroup().getId()))) .entrySet() .stream() .map(entry -> new Pair<>(entry.getKey(), entry.getValue().size())) .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond));
      
      







filterNot(多くの場合、メソッド参照を使用できます)、firstとfirstOrNullなどを分離します。そして、何かが足りない場合は、拡張機能を追加します(たとえば、BigDecimalシートの合計を追加します)。



怠け者



jsfを使用する場合、これは単なる救いです。 Jsfはしばしば同じフィールドをプルし(そしてそのためにデータベースにアクセスします)、ソートされたテーブルの場合、前回とまったく同じオブジェクトを返すことを期待しています。 そして最も重要なのは、レイジーは簡単に削除/挿入できることです。



スマートキャスト + 密閉クラス



コトリン



 fun rentForGroup(month: Date, group: ClassGroup): Int { val hall = group.hall val hallRent = hall.rent(month) return when (hallRent) { is Monthly -> hallRent.priceForMonth() / hall.groups(month).size is PercentOfRevenue -> hallRent.priceForMonth(creditForGroup(month, group)) is Hourly -> hallRent.priceForLessons(group.monthLessons(month)) } }
      
      





Java



 public int rentForGroup(Date month, ClassGroup group) { Hall hall = group.getHall(); Rent hallRent = hall.rent(month); if (hallRent instanceof Monthly) { return ((Monthly) hallRent).priceForMonth() / hall.groups(month).size(); } else if (hallRent instanceof PercentOfRevenue) { return ((PercentOfRevenue) hallRent).priceForMonth(creditForGroup(month, group)); } else if (hallRent instanceof Hourly) { return ((Hourly) hallRent).priceForLessons(group.monthLessons(month)); } else { throw new UnsupportedOperationException(); } }
      
      







インライン関数



Javaでは、これを行うのは不可能です(クラスに明示的なパラメーターを追加しない限り)。
 inline fun <reified E : Throwable> assertFail(expression: () -> Unit) { try { expression() Assert.fail("expression must fail with ${E::class.qualifiedName}") } catch (e: Throwable) { if (e !is E) { throw e } } } @Test fun greenTest() { assertFail<ArrayIndexOutOfBoundsException> { arrayOf(1, 2)[3] } }
      
      





 inline fun <reified T, ID: Serializable> CrudRepository<T, ID>.find(id: ID): T { return findOne(id) ?: throw ObjectNotFound(id, T::class.qualifiedName) }
      
      







文字列リテラル。



違いはわずかですが、間違いを犯す可能性ははるかに低く、インターネットから頭痛なくコピーアンドペーストすることもできます。



正規表現
コトリン



 val email = """^([_A-Za-z0-9-+]+(\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,}))?$"""
      
      





Java



 String email = "^([_A-Za-z0-9-+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,}))?$"
      
      







さらに、非常に素晴らしいパターン



後味



Kotlinに関するプロジェクトは1つだけで、小さな工芸品は数えていません。 ですから、私は自信を持って1つ言うことができます。Kotlin、Spring、DDDの要素はお互いを完全にサポートしています。 JavaのようにKotlinで記述した場合、構文上の砂糖(既に素晴らしい)を感じるだけですが、だれでも何かを挿入できるクラシックBeanを放棄すると(状態に実質的に制限がないことを意味します)、Kotlinは開花します。



UPD

Kotlinからの後味、パート1

Kotlinの後味、パート3。コルーチン-プロセッサー時間の共有



All Articles