JavaRush /Java Blog /Random-TL /Coffee break #155. Nangungunang 10 Function sa Java

Coffee break #155. Nangungunang 10 Function sa Java

Nai-publish sa grupo

Nangungunang 10 Function sa Java

Pinagmulan: DZone Ang artikulong ito ay naglilista ng sampung mga feature ng Java programming na kadalasang ginagamit ng mga developer sa kanilang pang-araw-araw na gawain. Coffee break #155.  Nangungunang 10 function sa Java - 1

1. Pamamaraan ng Pabrika ng Pagkolekta

Ang mga koleksyon ay isa sa mga pinakakaraniwang ginagamit na feature sa programming. Ginagamit ang mga ito bilang isang lalagyan kung saan nag-iimbak tayo ng mga bagay at ipinapasa ang mga ito. Ginagamit din ang mga koleksyon upang pagbukud-bukurin, hanapin, at ulitin ang mga bagay, na ginagawang mas madali ang buhay ng isang programmer. Mayroon silang ilang mga pangunahing interface tulad ng List, Set, Map, pati na rin ang ilang mga pagpapatupad. Ang tradisyunal na paraan ng paggawa ng Mga Koleksyon at Mapa ay maaaring mukhang verbose sa maraming developer. Iyon ang dahilan kung bakit ipinakilala ng Java 9 ang ilang maigsi na pamamaraan ng pabrika. Listahan :
List countries = List.of("Bangladesh", "Canada", "United States", "Tuvalu");
Itakda :
Set countries = Set.of("Bangladesh", "Canada", "United States", "Tuvalu");
Mapa :
Map countriesByPopulation = Map.of("Bangladesh", 164_689_383,
                                                            "Canada", 37_742_154,
                                                            "United States", 331_002_651,
                                                            "Tuvalu", 11_792);
Ang pamamaraan ng pabrika ay lubhang kapaki-pakinabang kapag gusto naming lumikha ng mga hindi nababagong lalagyan. Ngunit kung gagawa ka ng mga nababagong koleksyon, inirerekomendang gamitin ang tradisyonal na diskarte.

2. Lokal na Uri ng Hinuha

Ang Java 10 ay nagdagdag ng uri ng inference para sa mga lokal na variable. Bago ito, kinailangang tukuyin ng mga developer ang mga uri nang dalawang beses kapag nagdedeklara at nagpapasimula ng isang bagay. Sobrang nakakapagod. Tingnan ang sumusunod na halimbawa:
Map> properties = new HashMap<>();
Ang uri ng impormasyon sa magkabilang panig ay ipinahiwatig dito. Kung tutukuyin natin ito sa isang lugar, madaling mauunawaan ng code reader at ng Java compiler na dapat itong isang uri ng Map. Ginagawa iyon ng lokal na uri ng hinuha. Narito ang isang halimbawa:
var properties = new HashMap>();
Ngayon ang lahat ay nakasulat nang isang beses lamang at ang code ay hindi mukhang mas masahol pa. At kapag tumawag kami ng isang paraan at iniimbak ang resulta sa isang variable, ang code ay nagiging mas maikli. Halimbawa:
var properties = getProperties();
At higit pa:
var countries = Set.of("Bangladesh", "Canada", "United States", "Tuvalu");
Bagama't tila isang maginhawang tampok ang lokal na uri ng hinuha, pinupuna ito ng ilang tao. Nagtatalo ang ilang mga developer na binabawasan nito ang pagiging madaling mabasa. At ito ay mas mahalaga kaysa sa kaiklian.

3. Advanced na Switch Expressions

Ang tradisyonal na pahayag ng switch ay nasa Java mula pa noong una at nakapagpapaalaala sa C at C++ noon. Mabuti ito, ngunit habang umuunlad ang wika, hindi nag-alok sa amin ang operator na ito ng anumang mga pagpapahusay hanggang sa Java 14. Siyempre, mayroon itong ilang mga disadvantages. Ang pinakakilala ay ang fall -through: Upang malutas ang problemang ito, gumamit ang mga developer ng mga break statement, na higit sa lahat ay boilerplate code. Gayunpaman, ipinakilala ng Java 14 ang isang pinahusay na bersyon ng switch statement na may mas malaking listahan ng mga function. Ngayon hindi na namin kailangang magdagdag ng mga pahayag ng break at malulutas nito ang problema sa pagkabigo. Bukod pa rito, maaaring magbalik ng value ang switch statement, na nangangahulugang magagamit natin ito bilang expression at italaga ito sa isang variable.
int day = 5;
String result = switch (day) {
    case 1, 2, 3, 4, 5 -> "Weekday";
    case 6, 7 -> "Weekend";
    default -> "Unexpected value: " + day;
};

4. Mga tala

Bagama't ang Records ay isang medyo bagong feature na ipinakilala sa Java 16, maraming mga developer ang nakakakita nito na lubhang kapaki-pakinabang, pangunahin dahil sa paglikha ng mga hindi nababagong bagay. Kadalasan kailangan namin ng mga data object sa aming programa upang mag-imbak o magpasa ng mga halaga mula sa isang paraan patungo sa isa pa. Halimbawa, isang klase para sa paglilipat ng mga coordinate ng x, y at z, na isusulat namin tulad ng sumusunod:
package ca.bazlur.playground;

import java.util.Objects;

public final class Point {
    private final int x;
    private final int y;
    private final int z;

    public Point(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public int x() {
        return x;
    }

    public int y() {
        return y;
    }

    public int z() {
        return z;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (obj == null || obj.getClass() != this.getClass()) return false;
        var that = (Point) obj;
        return this.x == that.x &&
                this.y == that.y &&
                this.z == that.z;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y, z);
    }

    @Override
    public String toString() {
        return "Point[" +
                "x=" + x + ", " +
                "y=" + y + ", " +
                "z=" + z + ']';
    }

}
Mukhang masyadong verbose ang klase. Sa tulong ng mga entry, ang lahat ng code na ito ay maaaring mapalitan ng mas maigsi na bersyon:
package ca.bazlur.playground;

public record Point(int x, int y, int z) {
}

5. Opsyonal

Ang pamamaraan ay isang kontrata kung saan tinutukoy namin ang mga kondisyon. Tinukoy namin ang mga parameter sa kanilang uri, pati na rin ang uri ng pagbabalik. Pagkatapos ay inaasahan namin na kapag tinawag ang pamamaraan, ito ay kumilos ayon sa kontrata. Gayunpaman, kadalasan ay nauuwi tayo sa null mula sa isang paraan sa halip na isang halaga ng tinukoy na uri. Ito ay pagkakamali. Upang lutasin ito, karaniwang sinusubok ng initiator ang value na may kundisyon kung, anuman ang halaga o hindi. Halimbawa:
public class Playground {

    public static void main(String[] args) {
        String name = findName();
        if (name != null) {
            System.out.println("Length of the name : " + name.length());
        }
    }

    public static String findName() {
        return null;
    }
}
Tingnan ang code sa itaas. Ang paraan ng findName ay dapat na magbalik ng isang String , ngunit nagbabalik ito ng null. Dapat munang suriin ng nagpasimula ang mga null upang harapin ang problema. Kung ang nagpasimula ay nakalimutang gawin ito, pagkatapos ay makakakuha tayo ng NullPointerException . Sa kabilang banda, kung ang lagda ng pamamaraan ay nagpahiwatig ng posibilidad ng hindi pagbabalik, ito ay malulutas ang lahat ng kalituhan. At dito kami matutulungan ng Opsyonal .
import java.util.Optional;

public class Playground {

    public static void main(String[] args) {
        Optional optionalName = findName();
        optionalName.ifPresent(name -> {
            System.out.println("Length of the name : " + name.length());
        });
    }

    public static Optional findName() {
        return Optional.empty();
    }
}
Dito ay muling isinulat namin ang paraan ng findName na may Opsyonal na opsyon upang hindi magbalik ng anumang halaga. Inaalerto nito ang mga programmer nang maaga at inaayos ang problema.

6. Java Date Time API

Ang bawat developer ay nalilito sa isang antas o iba pa sa pagkalkula ng petsa at oras. Hindi ito pagmamalabis. Ito ay higit sa lahat dahil sa kakulangan ng isang mahusay na Java API para sa pagtatrabaho sa mga petsa at oras. Ngayon ang problemang ito ay hindi na nauugnay, dahil ipinakilala ng Java 8 ang isang mahusay na hanay ng mga API sa java.time package, na lumulutas sa lahat ng isyu na nauugnay sa petsa at oras. Ang java.time package ay may maraming mga interface at klase na nag-aalis ng karamihan sa mga problema, kabilang ang mga time zone. Ang pinakakaraniwang ginagamit na mga klase sa paketeng ito ay:
  • LocalDate
  • Lokal na Oras
  • LocalDateTime
  • Tagal
  • Panahon
  • ZonedDateTime
Isang halimbawa ng paggamit ng mga klase mula sa java.time package:
import java.time.LocalDate;
import java.time.Month;

public class Playground3 {
    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2022, Month.APRIL, 4);
        System.out.println("year = " + date.getYear());
        System.out.println("month = " + date.getMonth());
        System.out.println("DayOfMonth = " + date.getDayOfMonth());
        System.out.println("DayOfWeek = " + date.getDayOfWeek());
        System.out.println("isLeapYear = " + date.isLeapYear());
    }
}
Isang halimbawa ng paggamit ng klase ng LocalTime upang kalkulahin ang oras:
LocalTime time = LocalTime.of(20, 30);
int hour = time.getHour();
int minute = time.getMinute();
time = time.withSecond(6);
time = time.plusMinutes(3);
Pagdaragdag ng time zone:
ZoneId zone = ZoneId.of("Canada/Eastern");
LocalDate localDate = LocalDate.of(2022, Month.APRIL, 4);
ZonedDateTime zonedDateTime = date.atStartOfDay(zone);

7.NullPointerException

Kinamumuhian ng bawat developer ang NullPointerException. Maaari itong maging lalong mahirap kapag ang StackTrace ay hindi nagbibigay ng kapaki-pakinabang na impormasyon tungkol sa kung ano ang eksaktong problema. Upang ipakita ito, tingnan natin ang sample code:
package com.bazlur;

public class Main {

    public static void main(String[] args) {
        User user = null;
        getLengthOfUsersName(user);
    }

    public static void getLengthOfUsersName(User user) {
        System.out.println("Length of first name: " + user.getName().getFirstName());
    }
}

class User {
    private Name name;
    private String email;

    public User(Name name, String email) {
        this.name = name;
        this.email = email;
    }

   //getter
   //setter
}

class Name {
    private String firstName;
    private String lastName;

    public Name(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

   //getter
   //setter
}
Tingnan ang pangunahing paraan sa talatang ito. Nakita namin na ang isang NullPointerException ay susunod na itatapon . Kung tatakbo at isasama namin ang code sa isang bersyon bago ang Java 14, makukuha namin ang sumusunod na StackTrace:
Exception in thread "main" java.lang.NullPointerException
at com.bazlur.Main.getLengthOfUsersName(Main.java:11)
at com.bazlur.Main.main(Main.java:7)
Napakakaunting impormasyon dito tungkol sa kung saan at bakit nangyari ang NullPointerException . Ngunit sa Java 14 at mas bagong mga bersyon, nakakakuha kami ng higit pang impormasyon sa StackTrace, na napaka-maginhawa. Sa Java 14 makikita natin:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "ca.bazlur.playground.User.getName()" because "user" is null
at ca.bazlur.playground.Main.getLengthOfUsersName(Main.java:12)
at ca.bazlur.playground.Main.main(Main.java:8)

8. CompletableFuture

Nagsusulat kami ng mga programa sa linya sa pamamagitan ng linya, at ang mga ito ay karaniwang isinasagawa linya sa linya. Ngunit may mga pagkakataon na kailangan natin ng parallel execution para mas mapabilis ang programa. Para dito karaniwan naming ginagamit ang Java Thread. Ang Java thread programming ay hindi palaging tungkol sa parallel programming. Sa halip, binibigyan tayo nito ng kakayahang bumuo ng ilang independiyenteng mga module ng programa na isasagawa nang nakapag-iisa at madalas kahit na asynchronous. Gayunpaman, ang thread programming ay medyo mahirap, lalo na para sa mga nagsisimula. Ito ang dahilan kung bakit nag-aalok ang Java 8 ng isang mas simpleng API na nagbibigay-daan sa iyong magsagawa ng bahagi ng isang programa nang asynchronous. Tingnan natin ang isang halimbawa. Sabihin nating kailangan nating tumawag ng tatlong REST API at pagkatapos ay pagsamahin ang mga resulta. Maaari natin silang tawagan isa-isa. Kung ang bawat isa sa kanila ay tumatagal ng humigit-kumulang 200 millisecond, ang kabuuang oras para matanggap ang mga ito ay aabot ng 600 milliseconds. Paano kung maaari nating patakbuhin ang mga ito nang magkatulad? Dahil ang mga modernong processor ay karaniwang multi-core, madali nilang mahawakan ang tatlong rest call sa tatlong magkakaibang processor. Gamit ang CompletableFuture, madali nating magagawa ito.
package ca.bazlur.playground;

import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class SocialMediaService {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        var service = new SocialMediaService();

        var start = Instant.now();
        var posts = service.fetchAllPost().get();
        var duration = Duration.between(start, Instant.now());

        System.out.println("Total time taken: " + duration.toMillis());
    }

    public CompletableFuture> fetchAllPost() {
        var facebook = CompletableFuture.supplyAsync(this::fetchPostFromFacebook);
        var linkedIn = CompletableFuture.supplyAsync(this::fetchPostFromLinkedIn);
        var twitter = CompletableFuture.supplyAsync(this::fetchPostFromTwitter);

        var futures = List.of(facebook, linkedIn, twitter);

        return CompletableFuture.allOf(futures.toArray(futures.toArray(new CompletableFuture[0])))
                .thenApply(future -> futures.stream()
                        .map(CompletableFuture::join)
                        .toList());
    }
    private String fetchPostFromTwitter() {
        sleep(200);
        return "Twitter";
    }

    private String fetchPostFromLinkedIn() {
        sleep(200);
        return "LinkedIn";
    }

    private String fetchPostFromFacebook() {
        sleep(200);
        return "Facebook";
    }

    private void sleep(int millis) {
        try {
            TimeUnit.MILLISECONDS.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

9. Mga Ekspresyon ng Lambda

Ang mga expression ng Lambda ay marahil ang pinakamakapangyarihang katangian ng wikang Java. Binago nila ang paraan ng pagsusulat namin ng code. Ang isang lambda expression ay tulad ng isang hindi kilalang function na maaaring kumuha ng mga argumento at magbalik ng isang halaga. Maaari tayong magtalaga ng isang function sa isang variable at ipasa ito bilang mga argumento sa isang pamamaraan, at maibabalik ito ng pamamaraan. May katawan siya. Ang pagkakaiba lamang sa pamamaraan ay walang pangalan. Ang mga ekspresyon ay maikli at maigsi. Karaniwang hindi naglalaman ang mga ito ng maraming boilerplate code. Tingnan natin ang isang halimbawa kung saan kailangan nating ilista ang lahat ng mga file sa isang direktoryo na may extension na .java.
var directory = new File("./src/main/java/ca/bazlur/playground");
String[] list = directory.list(new FilenameFilter() {
    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(".java");
    }
});
Kung titingnan mong mabuti ang piraso ng code na ito, naipasa namin ang anonymous na inner class list() sa method . At sa panloob na klase inilagay namin ang lohika para sa pag-filter ng mga file. Mahalaga, interesado kami sa bahaging ito ng lohika, hindi sa pattern sa paligid ng lohika. Ang lambda expression ay nagpapahintulot sa amin na alisin ang buong template at maaari naming isulat ang code na interesado sa amin. Narito ang isang halimbawa:
var directory = new File("./src/main/java/ca/bazlur/playground");
String[] list = directory.list((dir, name) -> name.endsWith(".java"));
Siyempre, ito ay isa lamang halimbawa; ang mga expression ng lambda ay may maraming iba pang mga benepisyo.

10. Stream API

Sa aming pang-araw-araw na gawain, isa sa mga karaniwang gawain ay ang pagproseso ng isang set ng data. Mayroon itong ilang karaniwang operasyon tulad ng pag-filter, pagbabago, at pagkolekta ng mga resulta. Bago ang Java 8, ang mga naturang operasyon ay kinakailangan sa kalikasan. Kinailangan naming magsulat ng code para sa aming layunin (ibig sabihin, kung ano ang gusto naming makamit) at kung paano namin ito gustong gawin. Sa pag-imbento ng lambda expression at ng Stream API, maaari na tayong magsulat ng mga function sa pagpoproseso ng data nang deklaratibo. Ipinapahiwatig lamang namin ang aming intensyon, at hindi namin kailangang isulat kung paano namin nakuha ang resulta. Narito ang isang halimbawa: Mayroon kaming listahan ng mga aklat at gusto naming hanapin ang lahat ng pangalan ng mga aklat na Java, na pinaghihiwalay ng mga kuwit at pinagsunod-sunod.
public static String getJavaBooks(List books) {
    return books.stream()
            .filter(book -> Objects.equals(book.language(), "Java"))
            .sorted(Comparator.comparing(Book::price))
            .map(Book::name)
            .collect(Collectors.joining(", "));
}
Ang code sa itaas ay simple, nababasa at maigsi. Ngunit sa ibaba ay makakakita ka ng alternatibong imperative code:
public static String getJavaBooksImperatively(List books) {
    var filteredBook = new ArrayList();
    for (Book book : books) {
        if (Objects.equals(book.language(), "Java")){
            filteredBook.add(book);
        }
    }
    filteredBook.sort(new Comparator() {
        @Override
        public int compare(Book o1, Book o2) {
            return Integer.compare(o1.price(), o2.price());
        }
    });

    var joiner = new StringJoiner(",");
    for (Book book : filteredBook) {
        joiner.add(book.name());
    }

    return joiner.toString();
}
Bagama't ang parehong mga pamamaraan ay nagbabalik ng parehong halaga, malinaw nating nakikita ang pagkakaiba.
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION