Friday, July 14, 2017

Java SE 7 Kodunu Java SE 9'a Taşımak


Bu yazıda Java SE 7 ve öncesinde yazılmış bir uygulamada yer alan dizi ve torbalar üzerinde gerçekleştirilmiş döngülerin Java SE 9'a taşınması ile ilgili bir örnek ele alınacaktır. Aşağıda UML sınıf çizgesi verilen alan modelini önce Java 7'de kodlayalım:




Animal.java:

package com.example.animals.domain;

/**
 * 
 * @author Binnur KURT (binnur.kurt@gmail.com)
 *
 */
public abstract class Animal {
 protected int legs;

 public Animal(int legs) {
  this.legs = legs;
 }

 public int getLegs() {
  return legs;
 }

 public void walk() {
  System.out.println("Animal is walking now...");
 }

 public abstract void eat();
}

Pet.java:

package com.example.animals.domain;

/**
 * 
 * @author Binnur KURT (binnur.kurt@gmail.com)
 *
 */
public abstract interface Pet {
 void setName(String name);

 String getName();

 public abstract void play();
}

Spider.java:

package com.example.animals.domain;

/**
 * 
 * @author Binnur KURT (binnur.kurt@gmail.com)
 *
 */
public class Spider extends Animal {

 public Spider() {
  super(8);
 }

 @Override
 public void eat() {
  System.out.println("Spider is eating now...");
 }

 @Override
 public String toString() {
  return "Spider [legs=" + legs + "]";
 }

}

Cat.java:

package com.example.animals.domain;

/**
 * 
 * @author Binnur KURT (binnur.kurt@gmail.com)
 *
 */
public class Cat extends Animal implements Pet {
 private String name;

 public Cat() {
  this("Garfield");
 }

 public Cat(String name) {
  super(4);
  this.name = name;
 }

 @Override
 public void setName(String name) {
  this.name = name;
 }

 @Override
 public String getName() {
  return name;
 }

 @Override
 public void play() {
  System.out.println(name + " is playing now...");
 }

 @Override
 public void eat() {
  System.out.println(name + " is eating now...");
 }

 @Override
 public String toString() {
  return "Cat [name=" + name + ", legs=" + legs + "]";
 }

}

Fish.java:

package com.example.animals.domain;

/**
 * 
 * @author Binnur KURT (binnur.kurt@gmail.com)
 *
 */
public class Fish extends Animal implements Pet {
 private String name;

 public Fish() {
  this("Nemo");
 }

 public Fish(String name) {
  super(0);
  this.name = name;
 }

 @Override
 public void setName(String name) {
  this.name = name;
 }

 @Override
 public String getName() {
  return name;
 }

 @Override
 public void play() {
  System.out.println(name + " is playing now...");
 }

 @Override
 public void eat() {
  System.out.println(name + " is eating now...");
 }

 @Override
 public void walk() {
  System.out.println(name + " is swimming now...");
 }

 @Override
 public String toString() {
  return "Fish [name=" + name + ", legs=" + legs + "]";
 }

}

Şimdi bu alan modelini kullanan bir uygulama kodunu Java 7'de kodlayalım:

package com.example.animals.application;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.example.animals.domain.Animal;
import com.example.animals.domain.Cat;
import com.example.animals.domain.Fish;
import com.example.animals.domain.Pet;
import com.example.animals.domain.Spider;

/**
 * 
 * @author Binnur KURT (binnur.kurt@gmail.com)
 *
 */
public class AnimalAppInJava7 {

    public static void main(String[] args) {
        List<Animal> animals = Arrays.asList(
            new Spider(), 
            new Cat(), 
            new Fish("Free Willy"), 
            new Cat("Tekir"),                        
            new Fish("Jaws"), 
            new Spider()
        );
        
        for (Animal animal : animals) {
            animal.walk();
            animal.eat();
            if (animal instanceof Pet) {
                ((Pet) animal).play();
            }
        }
        
        List<Animal> wildAnimals = new ArrayList<>();
        for (Animal animal : animals) {
            if (!(animal instanceof Pet))
                wildAnimals.add(animal);
        }
        System.out.println("Wild Animals: "+wildAnimals);        
        
        List<Pet> pets = new ArrayList<>();
        for (Animal animal : animals) {
            if (animal instanceof Pet)
                pets.add((Pet) animal);
        }
        System.out.println("Pets: "+pets);
        
        int maxLegs= -1;
        for (Animal animal: animals){
            final int legs = animal.getLegs();
            if (legs>maxLegs) maxLegs= legs;
        }
        System.out.println("max number of legs: "+maxLegs);
    }

}

Java SE 7'de yazdığımız uygulamada ağırlıklı olarak Java SE 5'de gelen for döngüsünü kullanarak torbalar üzerinde çalışan döngüler kurduk. Şimdi birebir aynı işi yapan uygulamayı Java SE 9'da kodlayalım:

package com.example.animals.application;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.example.animals.domain.Animal;
import com.example.animals.domain.Cat;
import com.example.animals.domain.Fish;
import com.example.animals.domain.Pet;
import com.example.animals.domain.Spider;

/**
 * 
 * @author Binnur KURT (binnur.kurt@gmail.com)
 *
 */
public class AnimalAppInJava9 {

    public static void main(String[] args) {
        List<Animal> animals= List.of(
            new Spider(),
            new Cat(),
            new Fish("Free Willy"),
            new Cat("Tekir"),
            new Fish("Jaws"),
            new Spider()
        );

        Class<Pet> clazz = Pet.class;
        Consumer<Animal> walk = Animal::walk;
        Consumer<Animal> eat = Animal::eat;
        Consumer<Animal> playIfPet = animal -> {
            if (clazz.isInstance(animal)) 
                clazz.cast(animal).play();
        };
        animals.forEach(walk.andThen(eat).andThen(playIfPet));
        
        Predicate<? super Animal> isPet = animal -> animal instanceof Pet;
        animals.stream().filter(isPet).map(Pet.class::cast).forEach(Pet::play);

        List<Animal> pets= animals.stream()
                                  .filter(isPet)
                                  .collect(Collectors.toList());
        System.out.println("Pets: "+pets);
        
        Predicate<? super Animal> isWild= isPet.negate();
        List<Animal> wildAnimals= animals.stream()
                                    .filter(isWild)
                                    .collect(Collectors.toList());
        System.out.println("Wild animals: "+wildAnimals);

        animals.stream().map(Animal::getLegs)
                        .max(Integer::compare)
                        .ifPresent(System.out::println);
    }

}

Java 7 ve öncesinde uzun uzun yazdığımız döngüleri Java 9'da tek satırlık ifadelerle karşılamak mümkün:
  • Önce hayvanları yürüyüşe çıkaralım, ardından karınlarını doyuralım ve evcil olanları ile oyun oynayalım:
animals.forEach(walk.andThen(eat).andThen(playIfPet));
  • Evcil hayvanlar ile oyun oynayalım:
animals.stream().filter(Pet.class::isInstance).map(Pet.class::cast).forEach(Pet::play);
  • Vahşi hayvanların listesini alalım:
animals.stream().filter(isWild).collect(Collectors.toList());
  • Evcil hayvanların listesini alalım:
animals.stream().filter(isPet).collect(Collectors.toList());
  • En çok ayağa sahip hayvanda kaç adet ayak bulunur:
animals.stream().map(Animal::getLegs).max(Integer::compare).ifPresent(System.out::println);
  • Hayvanların toplam ayak sayısı:
int numberOfLegs= animals.stream().mapToInt(Animal::getLegs).sum();

Tek Satırlık İfadeler

Java SE 8'de gelen Fonksiyonel Programlama, Stream API ve MapReduce çatıları sayesinde çözümü tek satırlık ifadelerle oluşturabiliyoruz. Bu tek satırlık ifadelere örnekler oluşturabilmek amacıyla yukardıki örnek alan modelini kullanarak, farklı problemler çözelim.  

1. Java 9'da rastgele olarak 100 Animal türünden nesne ile dolu bir liste elde etmeye çalışalım:

List<Supplier<Animal>> constructors= List.of(Spider::new,Cat::new,Fish::new);
List<Animal> randomAnimals= 
          new Random().ints(0,constructors.size())
                      .limit(100)
                      .mapToObj(constructors::get)
                      .map( Supplier::get )
                      .collect( Collectors.toList() );
randomAnimals.forEach(System.out::println);

2. Her bir Animal türünden kaç adet nesne var sınıflandırmaya çalışalım:

Function<Animal, String> groupByClassName = animal -> animal.getClass().getSimpleName();
Map<String, Long> animalCountsByClassName = 
        randomAnimals.stream()
               .collect(
                   Collectors.groupingBy( groupByClassName, Collectors.counting() )
               );
System.out.println(animalCountsByClassName);

Yukarıdaki kod parçasını çalıştırdığımızda ekranda aşağıdaki gibi bir ileti alıyoruz:

{Cat=36, Spider=35, Fish=29}

3. Aynı ayak sayısına sahip hayvanların listesini alalım:

Function<Animal, String> classNameMapper = animal -> animal.getClass().getSimpleName();
Map<Integer, Set<String>> animalCountsByLegs = 
    randomAnimals.stream().collect(
        Collectors.groupingBy(
            Animal::getLegs,
            Collectors.mapping(classNameMapper,Collectors.toSet())
        )
    );        
System.out.println(animalCountsByLegs);

Yukarıdaki kod parçasını çalıştırdığımızda ekranda aşağıdaki gibi bir ileti alıyoruz:

{0=[Fish], 4=[Cat], 8=[Spider]}

4. Hayvanları önce ayak sayısına göre, ayak sayıları eşit olanları ise sınıf adına göre sıralı olacak şekilde ekrana yazmak istiyoruz:

final Comparator<Animal> compareByLegNumbers = (left,right) -> left.getLegs() - right.getLegs();
final Comparator<Animal> compareByClassName = (left,right) -> left.getClass().getSimpleName().compareTo(right.getClass().getSimpleName());
randomAnimals.stream().sorted(compareByLegNumbers.thenComparing(compareByClassName)).forEach(System.out::println);

5. Dört ayaklı evcil hayvanlar içerisinde en kalabalık türün adını bulmaya çalışalım:

final Predicate<? super Animal> isPet = animal -> animal instanceof Pet;        
final Comparator<Animal> compareByClassName = (left,right) -> left.getClass().getSimpleName().compareTo(right.getClass().getSimpleName());
final Function<Animal, String> classNameMapper = animal -> animal.getClass().getSimpleName();
final Comparator<Animal> compareByLegNumbers = (left,right) -> left.getLegs() - right.getLegs();
final Collector<Animal, ?, Map<String, Long>> clusterByAnimalTypeAndCountClusterSize = Collectors.groupingBy(classNameMapper,Collectors.counting());
final Comparator<Map.Entry<String, Long>> clusterSizeComparator = (e1,e2) -> Long.valueOf(e1.getValue() - e2.getValue()).intValue();
randomAnimals.stream()
             .filter(isPet)
             .collect(clusterByAnimalTypeAndCountClusterSize)
             .entrySet()
             .stream()
             .max(clusterSizeComparator)
             .map(Map.Entry::getKey)
             .ifPresent(System.out::println);

No comments:

Post a Comment