Javaの8関数型プログラミングの章XIII

関数型プログラミング中央アイデア:関数が別の関数にパラメータとして渡される、または戻り値として機能します。

プログラムメモリに合うように限られた、プログラマは、プログラムの実行時に異なる操作を実行するために、メモリ内のコードを変更することによって、コード・スペースを節約できます。この技術は、自己修正コード(自己書き換えコード)と呼ばれます

OO(オブジェクト指向、オブジェクト指向)は、抽象データです

FP(関数型プログラミング、関数型プログラミング)は抽象的行動であります

さらに、安全性の面で純粋な関数型言語。これは、すべてのデータは不変でなければならないという追加の制約を課す:一度設定、変更することはありません。

最も基本的な技術1つの並行プログラミングを解決するために、「不変オブジェクトと副作用なし」パラダイム

そのため、多くの場合、(他の可能な解決策がある)の並列プログラミングソリューションのための純粋な関数型言語として提唱。

1つのコントラスト古いものと新しいです

package functional;

interface Strategy {
    String approach(String msg);
}

class Soft implements Strategy {

    @Override
    public String approach(String msg) {
        return msg.toLowerCase() + "?";
    }
}

class Unrealted {
    static String twice(String msg) {
        return msg + " " + msg;
    }
}

public class Strategize {
    Strategy strategy;
    String msg;
    Strategize(String msg){
        strategy = new Soft(); // [1]在 Strategize 中,Soft 作为默认策略,在构造函数中赋值。
        this.msg = msg;
    }

    void communicate(){
        System.out.println(strategy.approach(msg));
    }
    void changeStategy(Strategy strategy){
        this.strategy = strategy;
    }

    public static void main(String[] args) {
        Strategy[] strategies = {
                new Strategy() {// [2]匿名内部类
                    @Override
                    public String approach(String msg) {
                        return msg.toUpperCase() + "!";
                    }
                },
                msg -> msg.substring(0,5),// [3]Java 8 的 Lambda 表达式
                // 由箭头 -> 分隔开参数和函数体,箭头左边是参数,箭头右侧是从 Lambda 返回的表达式,即函数体。这实现了与定义类、匿名内部类相同的效果,但代码少得多。
                Unrealted::twice// [4]Java 8 的方法引用,由 :: 区分。在 :: 的左边是类或对象的名称,在 :: 的右边是方法的名称,但没有参数列表。
        };
        Strategize s = new Strategize("Hello there");
        s.communicate();
        for (Strategy newStrategy : strategies){//遍历数组中的所有 Strategy
            s.changeStategy(newStrategy);// [5]将每个 Strategy 放入 变量 s 中
            s.communicate();// [6]产生不同的行为,具体取决于此刻正在使用的策略代码对象.我们传递的是行为,而非仅数据。
        }
    }
}

/*
hello there?
HELLO THERE!
Hello
Hello there Hello there
 */

2ラムダ式

package functional;

interface Description {
    String brief();
}

interface Body {
    String detailed(String head);
}

interface Multi {
    String twoArg(String head, Double d);
}

public class LambdaExpressions {
    static Body bod = h -> h + " No Parens!"; //当只用一个参数,可以不需要括号 ()。 然而,这是一个特例。
    static Body bod2 = (h) -> h + " More details"; //正常情况使用括号 () 包裹参数。 为了保持一致性,也可以使用括号 () 包裹单个参数,虽然这种情况并不常见。


    static Description desc = () -> "Short info"; //如果没有参数,则必须使用括号 () 表示空参数列表。
    static Multi mult = (h, n) -> h + n; // 对于多个参数,将参数列表放在括号 () 中。

    //到目前为止,所有 Lambda 表达式方法体都是单行。 该表达式的结果自动成为 Lambda 表达式的返回值,在此处使用 return 关键字是非法的。 这是 Lambda 表达式缩写用于描述功能的语法的另一种方式。
    static Description moreLines = () -> { //如果在 Lambda 表达式中确实需要多行,则必须将这些行放在花括号中。 在这种情况下,就需要使用 return。
        System.out.println("moreLines()");
        return "from moreLines()";
    };

    public static void main(String[] args) {
        System.out.println(bod.detailed("Oh"));
        System.out.println(bod2.detailed("Hi"));
        System.out.println(desc.brief());
        System.out.println(mult.twoArg("pi ", 3.14159));
        System.out.println(moreLines.brief());
    }
}
/*
Oh No Parens!
Hi More details
Short info
pi 3.14159
moreLines()
from moreLines()
 */

2.1再帰

再帰的なメソッドは、インスタンスまたは静的変数でなければならない、またはコンパイル時エラーが発生します。

package functional;

interface IntCall{
    int call(int arg);
}

階乗関数

package functional;

public class RecursiveFactorial {
    static IntCall fact; //fact 是一个静态变量

    public static void main(String[] args) {
        fact = n -> n == 0 ? 1: n* fact.call(n-1);
        for (int i = 0; i <=10; i++)
            System.out.println(fact.call(i));
    }
}
/*
1
1
2
6
24
120
720
5040
40320
362880
3628800
 */
package functional;

public class RecursiveFibonacci {
    IntCall fib; //实例变量

    RecursiveFibonacci() {
        fib = n -> n == 0 ? 0 :
                n == 1 ? 1 :
                        fib.call(n - 1) + fib.call(n - 2);
    }

    int fibonacci(int n) {
        return fib.call(n);
    }

    public static void main(String[] args) {
        RecursiveFibonacci rf = new RecursiveFibonacci();
        for (int i = 0; i <= 10; i++)
            System.out.println(rf.fibonacci(i));
    }
}

/*
0
1
1
2
3
5
8
13
21
34
55
 */

3メソッドリファレンス

Javaの8メソッドは歴史的な荷物を引用していません。メソッドの参照は:クラス名またはオブジェクト名が続く::して、メソッド名に従ってください。

package functional;

interface Callable { // [1]
    void call(String s);
}

class Describe {
    void show(String msg) {// [2] show() 的签名(参数类型和返回类型)符合 Callable 的 call() 的签名。
        System.out.println(msg);
    }
}

public class MethodReferences {
    static void hello(String name) {// [3]  hello() 也符合 call() 的签名。
        System.out.println("Hello," + name);
    }

    static class Description {
        String about;

        Description(String desc) {
            about = desc;
        }

        void help(String msg) {  // [4] //help() 也符合,它是静态内部类中的非静态方法。
            System.out.println(about + " " + msg);
        }
    }

    static class Helper {
        static void assist(String msg) {// [5] //assist() 是静态内部类中的静态方法。
            System.out.println(msg);
        }
    }

    public static void main(String[] args) {
        Describe d = new Describe();
        Callable c = d::show; // [6]  我们将 Describe 对象的方法引用赋值给 Callable ,它没有 show() 方法,而是 call() 方法。 但是,Java 似乎接受用这个看似奇怪的赋值,因为方法引用符合 Callable 的 call() 方法的签名。
        c.call("call()"); // [7] 可以通过调用 call() 来调用 show(),因为 Java 将 call() 映射到 show()。

        c = MethodReferences::hello; // [8] 静态方法引用
        c.call("Bob");

        c = new Description("valueable")::help; // [9] 这是 [6] 的另一个版本:对已实例化对象的方法的引用,有时称为绑定方法引用。
        c.call("information");

        c = Helper::assist;  // [10] 获取静态内部类的方法引用的操作与 [8] 中外部类方式一样
        c.call("Help!");
    }
}
/*
call()
Hello,Bob
valueable information
Help!
 */

3.1、Runnableインタフェース

package functional;

class Go{
    static void go(){
        System.out.println("Go::go()");
    }
}
public class RunnableMethodReference {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Anonymous");
            }
        }).start();

        new Thread(() -> System.out.println("lambda")).start();//Lambda 表达式

        new Thread(Go::go).start();//方法引用
    }
}
/*
Anonymous
lambda
Go::go()
 */

3.2参照方法アンバウンド

結合していないメソッド参照がオブジェクトに関連付けられていない通常の(非静的)メソッドです。結合していない参照を使用する前に、我々は、オブジェクトを提供する必要があります

package functional;

class X {
    String f() {
        return "X::f()";
    }
}

interface MakeString {
    String make();
}

interface TransformX {
    // 使用未绑定的引用时,函数方法的签名(接口中的单个方法)不再与方法引用的签名完全匹配。
    // 理由是:你需要一个对象来调用方法。
    String transform(X x);
}


public class UnboundMethodReference {
    public static void main(String[] args) {
        // 即使 make() 与 f() 具有相同的签名,编译也会报“invalid method reference”(无效方法引用)错误。
        // 这是因为实际上还有另一个隐藏的参数:我们的老朋友 this。 你不能在没有 X 对象的前提下调用 f()。
        // 因此,X :: f 表示未绑定的方法引用,因为它尚未“绑定”到对象
//        MakeString ms = X::f; // [1]
        TransformX sp = X::f;
        X x =new X();
        System.out.println(sp.transform(x));// [2] [2] 的结果有点像脑筋急转弯。 我接受未绑定的引用并对其调用 transform(),将其传递给 X,并以某种方式导致对 x.f() 的调用。 Java 知道它必须采用第一个参数,这实际上就是 this,并在其上调用方法。
        System.out.println(x.f());// 同等效果
    }
}
/*
X::f()
X::f()
 */

マルチパラメータの使用と組み合わせて未結合方法

package functional;

class This {
    void two(int i, double d) {
    }

    void three(int i, double d, String s) {
    }

    void four(int i, double d, String s, char c) {
    }
}

interface TwoArgs {
    void call2(This athis, int i, double d);
}

interface ThreeArgs {
    void call3(This athis,int i,double d, String s);
}

interface FourArgs{
    void call4(This athis,int i,double d,String s, char c);
}

public class MultiUnbound {
    public static void main(String[] args) {
        TwoArgs twoArgs = This::two;
        ThreeArgs threeArgs = This::three;
        FourArgs fourArgs = This::four;
        This athis = new This();
        twoArgs.call2(athis,11,3.14);
        threeArgs.call3(athis,11,3.14,"Three");
        fourArgs.call4(athis,11,3.14,"Four",'Z');
    }
}

3.2コンストラクタの参照

package functional;

class Dog {
    String name;
    int age = -1; // For "unknown"

    Dog() {
        name = "stray";
    }

    Dog(String nm) {
        name = nm;
    }

    Dog(String nm, int yrs) {
        name = nm;
        age = yrs;
    }
}

interface MakeNoArgs {
    Dog make();
}

interface Make1Arg {
    Dog make(String nm);
}

interface Make2Args {
    Dog make(String nm, int age);
}

public class CtorReference {
    public static void main(String[] args) {
        MakeNoArgs mna = Dog::new;// [1]
        Make1Arg m1a = Dog::new;// [2]
        Make2Args m2a = Dog::new;// [3]

        Dog dn = mna.make();
        Dog d1 = m1a.make("Comet");
        Dog d2 = m2a.make("Ralph",4);
    }
}
//我们如何对 [1],[2] 和 [3] 中的每一个使用 Dog :: new。 这 3 个构造函数只有一个相同名称::: new,但在每种情况下都赋值给不同的接口。编译器可以检测并知道从哪个构造函数引用。
//
//编译器能识别并调用你的构造函数( 在本例中为 make())。

4機能インタフェース

各インターフェースは、機能的方法とも称されるのみ抽象メソッドを含んでいます。

package functional;

@FunctionalInterface
interface Functional{
    String goodbye(String arg);
}

interface FunctionalNoAnn{
    String goodbye(String arg);
}

/*
@FunctionalInterface
interface NotFunctional{
    String goodbye(String arg);
    String hello(String arg);
}
产生错误信息:
        NotFunctional is not a functional interface
multiple non-overriding abstract methods
        found in interface NotFunctional
*/

public class FunctionalAnnotation {
    public String goodbye(String arg){
        return "Goodbye, " + arg;
    }

    public static void main(String[] args) {
        FunctionalAnnotation fa = new FunctionalAnnotation();
        // Java 8 在这里添加了一点小魔法:
        // 如果将方法引用或 Lambda 表达式赋值给函数式接口(类型需要匹配),
        // Java 会适配你的赋值到目标接口。
        // 编译器会自动包装方法引用或 Lambda 表达式到实现目标接口的类的实例中。
        Functional f = fa::goodbye;
        FunctionalNoAnn fna = fa::goodbye;
//        Functional fac = fa;// Incompatible
        Functional f1 = a -> "Goodbye, " + a;
        FunctionalNoAnn fnal = a -> "Goodbye, " + a;
    }
}

基本的な命名ガイドライン:

あなただけの代わりに、プリミティブ型のオブジェクトを扱う場合は、名前は機能、消費者、述語とのようでした。ジェネリック医薬品によって、パラメータの種類を追加します。

受信したパラメータは、基本的なタイプであれば、そのようLongConsumer、DoubleFunction、IntPredicateのような、基本的なタイプのサプライヤーの例外として、名前の最初の部分、によって表されます。

戻り値はToLongFunctionとによって表されるプリミティブ型である場合 そしてIntToLongFunction。

戻り値の型がパラメータ型と一致している場合、オペレータは:UnaryOperatorは、単一のパラメータ、二つのパラメータBinaryOperatorを使用します。

受信した2つのパラメータを返すブール値場合、述語(述部)です。

受信したパラメータの場合、2つの異なるタイプ、バイの名前。

以下の実施例はの機能ラムダ式のすべての種類の変種に基づいて列挙されています

package functional;

import java.util.function.*;

class Foo{}

class Bar{
    Foo f;
    Bar(Foo f){this.f = f;}
}

class IBaz{
    int i;
    IBaz(int i){
        this.i = i;
    }
}

class LBaz{
    long l;
    LBaz(long l){
        this.l = l;
    }
}
class DBaz{
    double d;
    DBaz(double d){
        this.d = d;
    }
}
public class FunctionVariants {
    static Function<Foo,Bar> f1 = f -> new Bar(f);
    static IntFunction<IBaz> f2 = i -> new IBaz(i);
    static LongFunction<LBaz> f3 = l ->new LBaz(l);
    static DoubleFunction<DBaz> f4 = d -> new DBaz(d);
    static ToIntFunction<IBaz> f5 = ib -> ib.i;
    static ToLongFunction<LBaz> f6 = lb -> lb.l;
    static ToDoubleFunction<DBaz> f7 = db -> db.d;
    static IntToLongFunction f8 = i -> i;
    static IntToDoubleFunction f9 = i -> i;
    static LongToIntFunction f10 = l -> (int)l;
    static LongToDoubleFunction f11 = l -> l;
    static DoubleToIntFunction f12 = d -> (int)d;
    static DoubleToLongFunction f13 = d -> (long)d;

    public static void main(String[] args) {
        Bar b = f1.apply(new Foo());
        IBaz ib = f2.apply(11);
        LBaz lb = f3.apply(11);
        DBaz db = f4.apply(11);
        int i = f5.applyAsInt(ib);
        long l = f6.applyAsLong(lb);
        double d = f7.applyAsDouble(db);
        l = f8.applyAsLong(12);
        d = f9.applyAsDouble(12);
        i = f10.applyAsInt(12);
        d = f11.applyAsDouble(12);
        i = f12.applyAsInt(13.0);
        l = f13.applyAsLong(13.0);
    }
}

メソッドの参照

package functional;

import java.util.function.BiConsumer;

class In1{}
class In2{}

public class MethodConversion {
    static void accept(In1 i1,In2 i2){ // 只要参数类型、返回类型与 BiConsumer 的 accept() 相同即可。
        System.out.println("accept()");
    }
    static void someOtherName(In1 i1,In2 i2){ //只要参数类型、返回类型与 BiConsumer 的 accept() 相同即可。
        System.out.println("someOtherName()");
    }

    public static void main(String[] args) {
        BiConsumer<In1,In2> bic;

        bic = MethodConversion::accept;
        bic.accept(new In1(),new In2());

        bic = MethodConversion::someOtherName;
        bic.accept(new In1(),new In2());
    }
}
/*
accept()
someOtherName()
 */

機能クラスベース、メソッドを基準に適用されます

単純な関数のシグネチャを作成します。

package functional;

import java.util.Comparator;
import java.util.function.*;

class AA{}
class BB{}
class CC{}

public class ClassFunctionals {
    static AA f1(){return new AA();}
    static int f2(AA aa1, AA aa2){ return 1;}
    static void f3(AA aa){}
    static void f4(AA aa, BB bb){}
    static CC f5(AA aa){return new CC();}
    static CC f6(AA aa, BB bb){return new CC();}
    static boolean f7(AA aa){return true;}
    static boolean f8(AA aa, BB bb){return true;}
    static AA f9(AA aa){return new AA();}
    static AA f10(AA aa1,AA aa2){return new AA();}

    public static void main(String[] args) {
        Supplier<AA> s = ClassFunctionals::f1;
        s.get();
        Comparator<AA> c = ClassFunctionals::f2;
        c.compare(new AA(),new AA());
        Consumer<AA> cons = ClassFunctionals::f3;
        cons.accept(new AA());
        BiConsumer<AA,BB> bicons = ClassFunctionals::f4;
        bicons.accept(new AA(),new BB());
        Function<AA,CC> f = ClassFunctionals::f5;
        CC cc = f.apply(new AA());
        BiFunction<AA,BB,CC> bif = ClassFunctionals::f6;
        cc = bif.apply(new AA(),new BB());
        Predicate<AA> p = ClassFunctionals::f7;
        boolean result = p.test(new AA());
        BiPredicate<AA,BB> bip = ClassFunctionals::f8;
        result = bip.test(new AA(),new BB());
        UnaryOperator<AA> uo = ClassFunctionals::f9;
        AA aa = uo.apply(new AA());
        BinaryOperator<AA> bo = ClassFunctionals::f10;
        aa = bo.apply(new AA(),new AA());
    }
}

以上4.1関数インタフェースパラメータ

package functional;

@FunctionalInterface
public interface TriFunction<T,U,V,R> {
    R apply(T t, U u, V v);
}

検証メソッドとラムダ式の参照をテストします

package functional;

public class TriFunctionTest {
    static int f(int i,long l,double d){return 99;}

    public static void main(String[] args) {
        TriFunction<Integer,Long,Double,Integer> tf = TriFunctionTest::f;
        tf = (i,l,d) -> 12;
    }
}

4.2は、関数の基本的な種類が欠けています

package functional;

import java.util.function.BiConsumer;

public class BitConsumerPermutations {
    static BiConsumer<Integer,Double> bicid =(i,d) -> System.out.printf("%d, %f%n",i,d); // %n 跨平台
    static BiConsumer<Double,Integer> bicdi = (d,i) -> System.out.printf("%d, %f%n",i,d);
    static BiConsumer<Integer,Long> bicil = (i,  l) -> System.out.printf("%d, %d%n",i,l);

    public static void main(String[] args) {
        bicid.accept(47,11.34);
        bicdi.accept(22.45,92);
        bicil.accept(1,11L);
    }
}
/*
47, 11.340000
92, 22.450000
1, 11
 */
package functional;

import java.util.function.Function;
import java.util.function.IntToDoubleFunction;

public class FunctionWithWrapped {
    public static void main(String[] args) {
//        Function<Integer,Double> fid = i -> i; // Integer cannot be converted to Double
        Function<Integer,Double> fid = i -> (double)i;
        IntToDoubleFunction fid2 = i -> i;
    }
}

5高次関数

高階関数(高階関数)消費または生成機能のほんの関数です。

产生函数

package functional;

import java.util.function.Function;

interface FuncSS extends Function<String,String> {} // [1] 继承

public class ProduceFunction {
    static FuncSS produce(){
        return s -> s.toLowerCase(); // [2] lambda表达式
    }

    public static void main(String[] args) {
        FuncSS f = produce();
        System.out.println(f.apply("YELLING"));
    }
}

消費関数

package functional;

import java.util.function.Function;

class One{}
class Two{}

public class ConsumeFunction {
    static Two consume(Function<One,Two> onetwo){
        return onetwo.apply(new One());
    }

    public static void main(String[] args) {
        Two two = consume(one -> new Two());
    }
}
package functional;

import java.util.function.Function;

class I {
    public I(){
        System.out.println("Create I");
    }
    @Override
    public String toString(){
        return "I";
    }
}

class O{
    public O(){
        System.out.println("Create O");
    }
    @Override
    public String toString(){
        return "O";
    }
}
public class TransformFunction {
    static Function<I,O> transform(Function<I,O> in){
        System.out.println("2");
        return in.andThen(o -> {
            System.out.println(o);
            return o;
        });
    }

    public static void main(String[] args) {
        System.out.println("1");
        Function<I,O> f2 = transform(i -> {
            System.out.println("lalla");
            System.out.println(i);
            return new O();
        });
        System.out.println("3");
        O o = f2.apply(new I());
    }
}
/*
1
2
3
Create I
lalla
I
Create O
 */

6閉鎖

内部型の閉鎖など6.1

匿名内部クラスのオーバーライドの例を使用する前に

package functional;

import java.util.function.IntSupplier;

public class AnonymousClosure {
    IntSupplier makeFun(int x){
        int i = 0;
        // 同样规则的应用:
        // i++; // 非等同 final 效果
        // x++; // 同上
        return new IntSupplier() {
            @Override
            public int getAsInt() {
                return x + i ;
            }
        };
    }
}

7関数組成物

package functional;

import java.util.function.Function;

public class FunctionComposition {
    static Function<String,String> f1 = s -> {
        System.out.println(s);
        return s.replace('A','_');
    },
    f2 = s -> s.substring(3),
    f3 = s -> s.toLowerCase(),
    f4 = f1.compose(f2).andThen(f3); // 创建的新函数 f4。它调用 apply() 的方式与常规几乎无异.
   // 当 f1 获得字符串时,它已经被f2 剥离了前三个字符。这是因为 compose(f2) 表示 f2 的调用发生在 f1 之前。

    public static void main(String[] args) {
        System.out.println(f4.apply("GO AFTER ALL AMBULANCES"));
    }
}
/*
AFTER ALL AMBULANCES
_fter _ll _mbul_nces
 */
package functional;

import java.util.function.Predicate;
import java.util.stream.Stream;

public class PredicateComposition {
    static Predicate<String>
            p1 = s -> s.contains("bar"),
    p2 = s -> s.length() < 5,
    p3 = s -> s.contains("foo"),
    p4 = p1.negate().and(p2).or(p3);

    public static void main(String[] args) {
        Stream.of("bar","foobar","foobaz","fongopuckey")
                .filter(p4)
                .forEach(System.out::println);
    }
}
/*
foobar
foobaz
 */

8カリーと部分評価

カリー化発明ハスケル・カリーのその名のいずれかから(カリー化)

複数のパラメータの関数は、パラメータは、単一の機能のシリーズに変換されます。

package functional;

import java.util.function.Function;

public class CurryingAndPartials {
    // 未柯里化
    static String uncurried(String a,String b){
        return a + b;
    }

    public static void main(String[] args) {
        // 柯里化的函数
        Function<String,Function<String,String>> sum = a -> b -> a + b; //[1] 这一连串的箭头很巧妙。注意,在函数接口声明中,第二个参数是另一个函数。

        System.out.println(uncurried("Hi","Ho"));

        Function<String,String> hi = sum.apply("Hi"); // [2]柯里化的目的是能够通过提供一个参数来创建一个新函数,所以现在有了一个“带参函数”和剩下的 “无参函数” 。实际上,你从一个双参数函数开始,最后得到一个单参数函数。
        System.out.println(hi.apply("Ho"));

        //部分应用:
        Function<String,String> sumHi = sum.apply("Hup ");
        System.out.println(sumHi.apply("Ho"));
        System.out.println(sumHi.apply("Hey"));
    }
}
/*HiHo
HiHo
Hup Ho
Hup Hey
 */

三パラメーター関数カリー化のレベルに追加することによって

package functional;

import java.util.function.Function;

public class Curry3Args {
    public static void main(String[] args) {
        Function<String,
                Function<String,
                        Function<String,String>>> sum =
                a -> b -> c -> a + b + c;
        Function<String,
                Function<String,String>> hi = sum.apply("Hi ");
        Function<String,String> ho = hi.apply("Ho ");
        System.out.println(ho.apply("Hup"));
    }
}
/*
对于每个级别的箭头级联(Arrow-cascading),你在类型声明中包裹了另一个 Function。
Hi Ho Hup
 */

基本的な処理と梱包タイプは、適切なインタフェース機能を使用します

package functional;

import java.util.function.IntFunction;
import java.util.function.IntUnaryOperator;

public class CurriedIntAdd {
    public static void main(String[] args) {
        IntFunction<IntUnaryOperator> curriedIntAdd = a -> b -> a + b;
        IntUnaryOperator add4 = curriedIntAdd.apply(4);
        System.out.println(add4.applyAsInt(5));
    }
}

9純粋に機能的なプログラミング

必ずすべてのものを作るためには最終的なものではなく、副作用のないあなたの方法や機能のすべて。Javaは、本質的には、不変の言語ではありませんので、我々は、コンパイラによって、トラブルシューティングすることはできません。

10まとめ

おすすめ

転載: www.cnblogs.com/erinchen/p/12310374.html