Pengenalan kepada JOOL

1. Gambaran keseluruhan

Dalam artikel ini, kita akan melihat JOOLperpustakaan - produk lain dari jOOQ.

2. Ketergantungan Maven

Mari mulakan dengan menambahkan kebergantungan Maven ke pom.xml anda :

 org.jooq jool 0.9.12  

Anda boleh mendapatkan versi terkini di sini.

3. Antara Muka Berfungsi

Di Java 8, antara muka fungsional agak terhad. Mereka menerima maksimum dua parameter dan tidak mempunyai banyak ciri tambahan.

jOOL memperbaikinya dengan membuktikan satu set antara muka fungsional baru yang dapat menerima genap 16 parameter (dari Function1 hingga Function16) dan diperkaya dengan kaedah tambahan yang berguna.

Sebagai contoh, untuk membuat fungsi yang memerlukan tiga argumen, kita dapat menggunakan Function3:

Function3 lengthSum = (v1, v2, v3) -> v1.length() + v2.length() + v3.length();

Di Jawa tulen, anda perlu melaksanakannya sendiri. Selain itu, antara muka fungsional dari jOOL mempunyai kaedah yang berlakuPartial () yang membolehkan kita melakukan aplikasi separa dengan mudah:

Function2 addTwoNumbers = (v1, v2) -> v1 + v2; Function1 addToTwo = addTwoNumbers.applyPartially(2); Integer result = addToTwo.apply(5); assertEquals(result, (Integer) 7);

Apabila kita mempunyai kaedah jenis Function2 , kita dapat mengubahnya dengan mudah menjadi Java BiFunction standard dengan menggunakan kaedah toBiFunction () :

BiFunction biFunc = addTwoNumbers.toBiFunction();

Begitu juga, terdapat kaedah toFunction () dalam jenis Function1 .

4. Tuples

Tuple adalah konstruk yang sangat penting dalam dunia pengaturcaraan yang berfungsi. Ini adalah bekas yang ditaip untuk nilai di mana setiap nilai boleh mempunyai jenis yang berbeza. Tuples sering digunakan sebagai argumen fungsi .

Mereka juga sangat berguna ketika melakukan transformasi pada aliran peristiwa. Di JOOL, kami mempunyai tupel yang dapat membungkus dari satu hingga enam belas nilai, yang disediakan oleh Tuple1 hingga jenis Tuple16 :

tuple(2, 2)

Dan untuk empat nilai:

tuple(1,2,3,4); 

Mari kita pertimbangkan satu contoh apabila kita mempunyai urutan tupel yang membawa 3 nilai:

Seq
    
      personDetails = Seq.of( tuple("michael", "similar", 49), tuple("jodie", "variable", 43)); Tuple2 tuple = tuple("winter", "summer"); List
     
       result = personDetails .map(t -> t.limit2().concat(tuple)).toList(); assertEquals( result, Arrays.asList(tuple("michael", "similar", "winter", "summer"), tuple("jodie", "variable", "winter", "summer")) );
     
    

Kita boleh menggunakan pelbagai jenis transformasi pada tupel. Pertama, kita memanggil kaedah limit2 () untuk mengambil hanya dua nilai dari Tuple3. Kemudian, kita memanggil kaedah concat () untuk menggabungkan dua tupel.

Hasilnya, kami mendapat nilai dari jenis Tuple4 .

5. Rujukan

The Seq membina menambah kaedah peringkat yang lebih tinggi pada Stream manakala sering menggunakan kaedah-kaedah di bawahnya.

5.1. Mengandungi Operasi

Kami dapat menemui beberapa varian kaedah memeriksa keberadaan elemen dalam Seq. Beberapa kaedah menggunakan kaedah anyMatch () dari kelas Strim :

assertTrue(Seq.of(1, 2, 3, 4).contains(2)); assertTrue(Seq.of(1, 2, 3, 4).containsAll(2, 3)); assertTrue(Seq.of(1, 2, 3, 4).containsAny(2, 5)); 

5.2. Sertai Operasi

Apabila kita mempunyai dua aliran dan kita mahu menyertainya (serupa dengan operasi gabungan SQL dari dua set data), menggunakan kelas Aliran standard bukanlah cara yang sangat elegan untuk melakukan ini:

Stream left = Stream.of(1, 2, 4); Stream right = Stream.of(1, 2, 3); List rightCollected = right.collect(Collectors.toList()); List collect = left .filter(rightCollected::contains) .collect(Collectors.toList()); assertEquals(collect, Arrays.asList(1, 2));

Kita perlu mengumpulkan aliran kanan ke senarai, untuk mengelakkan java.lang.IllegalStateException: aliran telah dikendalikan atau ditutup. Seterusnya, kita perlu membuat operasi kesan sampingan dengan mengakses senarai RightCollected dari kaedah penapis . Ini adalah cara yang ralat dan tidak elegan untuk menggabungkan dua set data.

Nasib baik, Seq mempunyai kaedah yang berguna untuk melakukan gabungan dalaman, kiri dan kanan pada set data. Kaedah-kaedah tersebut menyembunyikan pelaksanaannya dengan memperlihatkan API yang elegan.

Kita boleh melakukan penyertaan dalaman dengan menggunakan kaedah innerJoin () :

assertEquals( Seq.of(1, 2, 4).innerJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(), Arrays.asList(tuple(1, 1), tuple(2, 2)) );

Kita boleh melakukan penyatuan kanan dan kiri:

assertEquals( Seq.of(1, 2, 4).leftOuterJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(), Arrays.asList(tuple(1, 1), tuple(2, 2), tuple(4, null)) ); assertEquals( Seq.of(1, 2, 4).rightOuterJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(), Arrays.asList(tuple(1, 1), tuple(2, 2), tuple(null, 3)) );

Bahkan ada kaedah crossJoin () yang memungkinkan untuk membuat gabungan kartesian dari dua set data:

assertEquals( Seq.of(1, 2).crossJoin(Seq.of("A", "B")).toList(), Arrays.asList(tuple(1, "A"), tuple(1, "B"), tuple(2, "A"), tuple(2, "B")) );

5.3. Manipulating a Seq

Seq has many useful methods for manipulating sequences of elements. Let's look at some of them.

We can use a cycle() method to take repeatedly elements from a source sequence. It will create an infinite stream, so we need to be careful when collecting results to a list thus we need to use a limit() method to transform infinite sequence into finite one:

assertEquals( Seq.of(1, 2, 3).cycle().limit(9).toList(), Arrays.asList(1, 2, 3, 1, 2, 3, 1, 2, 3) );

Let's say that we want to duplicate all elements from one sequence to the second sequence. The duplicate() method does exactly that:

assertEquals( Seq.of(1, 2, 3).duplicate().map((first, second) -> tuple(first.toList(), second.toList())), tuple(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3)) ); 

Returning type of a duplicate() method is a tuple of two sequences.

Let's say that we have a sequence of integers and we want to split that sequence into two sequences using some predicate. We can use a partition() method:

assertEquals( Seq.of(1, 2, 3, 4).partition(i -> i > 2) .map((first, second) -> tuple(first.toList(), second.toList())), tuple(Arrays.asList(3, 4), Arrays.asList(1, 2)) );

5.4. Grouping Elements

Grouping elements by a key using the Stream API is cumbersome and non-intuitive – because we need to use collect() method with a Collectors.groupingBy collector.

Seq hides that code behind a groupBy() method that returns Map so there is no need to use a collect() method explicitly:

Map
    
      expectedAfterGroupBy = new HashMap(); expectedAfterGroupBy.put(1, Arrays.asList(1, 3)); expectedAfterGroupBy.put(0, Arrays.asList(2, 4)); assertEquals( Seq.of(1, 2, 3, 4).groupBy(i -> i % 2), expectedAfterGroupBy );
    

5.5. Skipping Elements

Let's say that we have a sequence of elements and we want to skip elements while a predicate is not matched. When a predicate is satisfied, elements should land in a resulting sequence.

We can use a skipWhile() method for that:

assertEquals( Seq.of(1, 2, 3, 4, 5).skipWhile(i -> i < 3).toList(), Arrays.asList(3, 4, 5) );

We can achieve the same result using a skipUntil() method:

assertEquals( Seq.of(1, 2, 3, 4, 5).skipUntil(i -> i == 3).toList(), Arrays.asList(3, 4, 5) );

5.6. Zipping Sequences

When we're processing sequences of elements, often there is a need to zip them into one sequence.

The zip() API that could be used to zip two sequences into one:

assertEquals( Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c")).toList(), Arrays.asList(tuple(1, "a"), tuple(2, "b"), tuple(3, "c")) );

The resulting sequence contains tuples of two elements.

When we are zipping two sequences, but we want to zip them in a specific way we can pass a BiFunction to a zip() method that defines the way of zipping elements:

assertEquals( Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c"), (x, y) -> x + ":" + y).toList(), Arrays.asList("1:a", "2:b", "3:c") );

Sometimes, it is useful to zip sequence with an index of elements in this sequence, via the zipWithIndex() API:

assertEquals( Seq.of("a", "b", "c").zipWithIndex().toList(), Arrays.asList(tuple("a", 0L), tuple("b", 1L), tuple("c", 2L)) );

6. Converting Checked Exceptions to Unchecked

Let's say that we have a method that takes a string and can throw a checked exception:

public Integer methodThatThrowsChecked(String arg) throws Exception { return arg.length(); }

Then we want to map elements of a Stream applying that method to each element. There is no way to handle that exception higher so we need to handle that exception in a map() method:

List collect = Stream.of("a", "b", "c").map(elem -> { try { return methodThatThrowsChecked(elem); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } }).collect(Collectors.toList()); assertEquals( collect, Arrays.asList(1, 1, 1) );

There is not much we can do with that exception because of the design of functional interfaces in Java so in a catch clause, we are converting a checked exception into unchecked one.

Fortunately, in a jOOL there is an Unchecked class that has methods that can convert checked exceptions into unchecked exceptions:

List collect = Stream.of("a", "b", "c") .map(Unchecked.function(elem -> methodThatThrowsChecked(elem))) .collect(Collectors.toList()); assertEquals( collect, Arrays.asList(1, 1, 1) );

Kami sedang menyelesaikan panggilan ke kaedah MethodThatThrowsChecked () ke kaedah Unchecked.function () yang menangani penukaran pengecualian di bawahnya.

7. Kesimpulannya

Artikel ini menunjukkan cara menggunakan pustaka jOOL yang menambahkan kaedah tambahan yang berguna ke API Aliran standard Java .

Pelaksanaan semua contoh dan coretan kod ini terdapat dalam projek GitHub - ini adalah projek Maven, jadi mudah untuk diimport dan dijalankan sebagaimana adanya.