Android上使用Lombok

Android上使用Lombok


[TOC]

簡介

最近幾天嘗試了一把后端的工作,發(fā)現(xiàn)后端同學(xué)使用了一個第三庫——Lombok,用了一下,感覺還不錯,特來介紹一下,感覺和以前介紹過的AutoValue挺像的。

Lombok 官網(wǎng)上面有個幾分鐘的視頻,接單介紹了Lombok的用途,使用方法很簡單,只需要依賴對應(yīng)的jar文件,然后在對應(yīng)的Java文件上使用注解即可。

先看個例子,下面是常見的一個Java一個實體類,含有field、setter、getter、equals、hashcode、toString方法。

public class User {

    private int id;

    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (id != user.id) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

如果使用了Lombok,就很簡單了,直接定義好字段,然后添加一個注解@Data即可,其他方法,工具自動生成,雖然上面的方法我們也是用工具生成的,但是如果要添加或者刪除字段,還是要修改代碼的,如果直接使用注解的方式,那么還是簡單的,無需修改任何方法。

@Data
public class UserLombok {

    private int id;

    private String name;
}

注解簡介

Lombok 主要使用就是通過添加注解,來自動生成代碼,主要包含兩類,一種是Stable類型,一種是Experimental。前面表示穩(wěn)定的注解,后面表示實驗類型的,可能會被移除。本文主要介紹Stable類型,Experimental由于使用較少,不做講解。

Stable

  • val

Finally! Hassle-free final local variables.

  • @NonNull

or: How I learned to stop worrying and love the NullPointerException.

  • @Cleanup

Automatic resource management: Call your close() methods safely with no hassle.

  • @Getter/@Setter

Never write public int getFoo() {return foo;} again.

  • @ToString

No need to start a debugger to see your fields: Just let lombok generate a toString for you!

  • @EqualsAndHashCode

Equality made easy: Generates hashCode and equals implementations from the fields of your object..

  • @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor

Constructors made to order: Generates constructors that take no arguments, one argument per final / non-nullfield, or one argument for every field.

  • @Data

All together now: A shortcut for @ToString, @EqualsAndHashCode, @Getter on all fields, and @Setter on all non-final fields, and @RequiredArgsConstructor!

  • @Value

Immutable classes made very easy.

  • @Builder

... and Bob's your uncle: No-hassle fancy-pants APIs for object creation!

  • @SneakyThrows

To boldly throw checked exceptions where no one has thrown them before!

  • @Synchronized

synchronized done right: Don't expose your locks.

  • @Getter(lazy=true)

Laziness is a virtue!

  • @Log

Captain's Log, stardate 24435.7: "What was that line again?"

Experimental

  • var

Modifiable local variables with a type inferred by assigning value.

  • @Accessors

A more fluent API for getters and setters.

  • @ExtensionMethod

Annoying API? Fix it yourself: Add new methods to existing types!

  • @FieldDefaults

New default field modifiers for the 21st century.

  • @Delegate

Don't lose your composition.

  • @Wither

Immutable 'setters' - methods that create a clone but with one changed field.

  • onMethod= / onConstructor= / onParam=

Sup dawg, we heard you like annotations, so we put annotations in your annotations so you can annotate while you're annotating.

  • @UtilityClass

Utility, metility, wetility! Utility classes for the masses.

  • @Helper

With a little help from my friends... Helper methods for java.

Android 集成

項目根目錄下面新建配置文件 lombok.config,同時填上對應(yīng)的配置項,Java項目不需要,Android和Java還是有點區(qū)別的,不配置有的注解使用不了,編譯不過。

lombok.config

lombok.anyConstructor.suppressConstructorProperties=true

然后在對應(yīng)的項目中添加gradle依賴就行了。

dependencies {
    provided "org.projectlombok:lombok:1.16.18"
    compile 'org.glassfish:javax.annotation:10.0-b28'
}

可以在Android Studio中安裝lombok插件。

這樣可以很方便的看到類中生成的方法

注解說明

下面簡單說明注解的使用方法(如需了解詳細使用,請參閱官方文檔),以及使用注解后類中生成的方法。

val

定義一個final類型的變量,并且可以不寫類型。

如:

public class ValExample {

    public String example() {
        val example = new ArrayList<String>();
        example.add("Hello, World!");
        val foo = example.get(0);
        return foo.toLowerCase();
    }

    public void example2() {
        val map = new HashMap<Integer, String>();
        map.put(0, "zero");
        map.put(5, "five");
        for (val entry : map.entrySet()) {
            System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
        }
    }
}

class字節(jié)碼:

public class ValExample {
    public ValExample() {
    }

    public String example() {
        ArrayList<String> example = new ArrayList();
        example.add("Hello, World!");
        String foo = (String)example.get(0);
        return foo.toLowerCase();
    }

    public void example2() {
        HashMap<Integer, String> map = new HashMap();
        map.put(Integer.valueOf(0), "zero");
        map.put(Integer.valueOf(5), "five");
        Iterator var2 = map.entrySet().iterator();

        while(var2.hasNext()) {
            Entry<Integer, String> entry = (Entry)var2.next();
            System.out.printf("%d: %s\n", new Object[]{entry.getKey(), entry.getValue()});
        }
    }
}

@NonNull

非空值判斷,如果為空,則拋出異常

如:

public class NonNullExample {

    public static int length(@NonNull String string) {
        return string.length();
    }
}

class字節(jié)碼

public class NonNullExample {
    public NonNullExample() {
    }

    public static int length(@NonNull String string) {
        if(string == null) {
            throw new NullPointerException("string");
        } else {
            return string.length();
        }
    }
}

@Cleanup

可以自動調(diào)用close方法

public class CleanupExample {

    public static void main(String[] args) throws IOException {
        @Cleanup InputStream in = new FileInputStream(args[0]);
        @Cleanup OutputStream out = new FileOutputStream(args[1]);
        byte[] b = new byte[10000];
        while (true) {
            int r = in.read(b);
            if (r == -1) break;
            out.write(b, 0, r);
        }
    }
}

class字節(jié)碼

public class CleanupExample {
    public CleanupExample() {
    }

    public static void main(String[] args) throws IOException {
        FileInputStream in = new FileInputStream(args[0]);

        try {
            FileOutputStream out = new FileOutputStream(args[1]);

            try {
                byte[] b = new byte[10000];

                while(true) {
                    int r = in.read(b);
                    if(r == -1) {
                        return;
                    }

                    out.write(b, 0, r);
                }
            } finally {
                if(Collections.singletonList(out).get(0) != null) {
                    out.close();
                }

            }
        } finally {
            if(Collections.singletonList(in).get(0) != null) {
                in.close();
            }

        }
    }
}

@Getter/@Setter

自動生成setter、getter方法

// GetterSetterExample.java
public class GetterSetterExample {

    @Getter
    @Setter
    private int age;

    @Setter(AccessLevel.PROTECTED)
    private String name;
}

// GetterSetterExample.class
public class GetterSetterExample {
    private int age;
    private String name;

    public GetterSetterExample() {
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    protected void setName(String name) {
        this.name = name;
    }
}

@ToString

自動生成toString方法

// ToStringExample.java
@ToString(exclude = "id")
public class ToStringExample {

    private int id;

    private String name;

    private String passwd;

    public ToStringExample(int id, String name, String passwd) {
        this.id = id;
        this.name = name;
        this.passwd = passwd;
    }
}

// ToStringExample.class
public class ToStringExample {
    private int id;
    private String name;
    private String passwd;

    public ToStringExample(int id, String name, String passwd) {
        this.id = id;
        this.name = name;
        this.passwd = passwd;
    }

    public String toString() {
        return "ToStringExample(name=" + this.name + ", passwd=" + this.passwd + ")";
    }
}

@EqualsAndHashCode

自動生成equals和hashcode方法。

// EqualsAndHashCodeExample.java
@EqualsAndHashCode
public class EqualsAndHashCodeExample {

    private int id;

    private String name;

    public EqualsAndHashCodeExample(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

// EqualsAndHashCodeExample.class
public class EqualsAndHashCodeExample {
    private int id;
    private String name;

    public EqualsAndHashCodeExample(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public boolean equals(Object o) {
        if(o == this) {
            return true;
        } else if(!(o instanceof EqualsAndHashCodeExample)) {
            return false;
        } else {
            EqualsAndHashCodeExample other = (EqualsAndHashCodeExample)o;
            if(!other.canEqual(this)) {
                return false;
            } else if(this.id != other.id) {
                return false;
            } else {
                Object this$name = this.name;
                Object other$name = other.name;
                if(this$name == null) {
                    if(other$name != null) {
                        return false;
                    }
                } else if(!this$name.equals(other$name)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof EqualsAndHashCodeExample;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        int result = result * 59 + this.id;
        Object $name = this.name;
        result = result * 59 + ($name == null?43:$name.hashCode());
        return result;
    }
}

@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor

自動生成相關(guān)的構(gòu)造函數(shù)

// ConstructorExample.java
@ToString
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PUBLIC)
public class ConstructorExample<T> {

    private String args;

    @ToString
    @RequiredArgsConstructor(staticName = "of")
    public static class StaticMethodsExample {

        @NonNull
        private String field;
    }
}

// ConstructorExample.class
public class ConstructorExample<T> {
    private String args;

    public String toString() {
        return "ConstructorExample(args=" + this.args + ")";
    }

    public ConstructorExample() {
    }

    public ConstructorExample(String args) {
        this.args = args;
    }

    public static class StaticMethodsExample {
        @NonNull
        private String field;

        public String toString() {
            return "ConstructorExample.StaticMethodsExample(field=" + this.field + ")";
        }

        private StaticMethodsExample(@NonNull String field) {
            if(field == null) {
                throw new NullPointerException("field");
            } else {
                this.field = field;
            }
        }

        public static ConstructorExample.StaticMethodsExample of(@NonNull String field) {
            return new ConstructorExample.StaticMethodsExample(field);
        }
    }
}

@Builder

自動生成構(gòu)造者模式方法

// BuilderExample.java

@Builder
@Data
public class BuilderExample {

    private String name;

    private int age;

    @Singular
    private Set<String> occupations;
}

class文件太長,就不貼了,下面是調(diào)用方式。

// test builder
BuilderExample builderExample = BuilderExample.builder()
    .name("admin")
    .age(10)
    .occupation("aaa")
    .occupation("bbb")
    .build();

Log.i(TAG, "onCreate: " + builderExample);

@SneakyThrows

自動生成異常拋出代碼

// SneakyThrowsExample.java

public class SneakyThrowsExample implements Runnable {

    @SneakyThrows(UnsupportedEncodingException.class)
    public String utf8ToString(byte[] bytes) {
        return new String(bytes, "UTF-8");
    }

    @SneakyThrows
    public void run() {
        throw new Throwable();
    }
}

// SneakyThrowsExample.class
public class SneakyThrowsExample implements Runnable {
    public SneakyThrowsExample() {
    }

    public String utf8ToString(byte[] bytes) {
        try {
            return new String(bytes, "UTF-8");
        } catch (UnsupportedEncodingException var3) {
            throw var3;
        }
    }

    public void run() {
        try {
            throw new Throwable();
        } catch (Throwable var2) {
            throw var2;
        }
    }
}

@Synchronized

自動生成線程同步代碼

// SynchronizedExample.java
public class SynchronizedExample {

    private final Object readLock = new Object();

    @Synchronized
    public static void hello() {
        System.out.println("world");
    }

    @Synchronized
    public int answerToLife() {
        return 42;
    }

    @Synchronized("readLock")
    public void foo() {
        System.out.println("bar");
    }
}

// SynchronizedExample.class
public class SynchronizedExample {
    private static final Object $LOCK = new Object[0];
    private final Object $lock = new Object[0];
    private final Object readLock = new Object();

    public SynchronizedExample() {
    }

    public static void hello() {
        Object var0 = $LOCK;
        synchronized($LOCK) {
            System.out.println("world");
        }
    }

    public int answerToLife() {
        Object var1 = this.$lock;
        synchronized(this.$lock) {
            return 42;
        }
    }

    public void foo() {
        Object var1 = this.readLock;
        synchronized(this.readLock) {
            System.out.println("bar");
        }
    }
}

@Getter(lazy=true)

延遲初始化

// GetterLazyExample.java
public class GetterLazyExample {

    @Getter(lazy = true)
    private final double[] cached = expensive();

    private double[] expensive() {
        double[] result = new double[1000000];
        for (int i = 0; i < result.length; i++) {
            result[i] = Math.asin(i);
        }
        return result;
    }
}

// GetterLazyExample.class
public class GetterLazyExample {

    private final AtomicReference<Object> cached = new AtomicReference();

    public GetterLazyExample() {
    }

    private double[] expensive() {
        double[] result = new double[1000000];

        for(int i = 0; i < result.length; ++i) {
            result[i] = Math.asin((double)i);
        }

        return result;
    }

    public double[] getCached() {
        Object value = this.cached.get();
        if(value == null) {
            AtomicReference var2 = this.cached;
            synchronized(this.cached) {
                value = this.cached.get();
                if(value == null) {
                    double[] actualValue = this.expensive();
                    value = actualValue == null?this.cached:actualValue;
                    this.cached.set(value);
                }
            }
        }

        return (double[])((double[])(value == this.cached?null:value));
    }
}

@Log

自動生成日志對象,不過都是J2EE方面的,Android端用途不大。

官方示例

原理

自從Java 6起,javac就支持“JSR 269 Pluggable Annotation Processing API”規(guī)范,只要程序?qū)崿F(xiàn)了該API,就能在javac運行的時候得到調(diào)用。

舉例來說,現(xiàn)在有一個實現(xiàn)了"JSR 269 API"的程序A,那么使用javac編譯源碼的時候具體流程如下:

  1. javac對源代碼進行分析,生成一棵抽象語法樹(AST)

  2. 運行過程中調(diào)用實現(xiàn)了"JSR 269 API"的A程序

  3. 此時A程序就可以完成它自己的邏輯,包括修改第一步驟得到的抽象語法樹(AST)

  4. javac使用修改后的抽象語法樹(AST)生成字節(jié)碼文件

詳細的流程圖如下:

總結(jié)

綜上所述,使用了lombok可以簡化Java代碼,因為是在編譯期處理所以可能會增加點時間,不過對于Android來說,可以嘗試一下,不過17年Google IO已經(jīng)推薦使用Kotlin開發(fā)Android了,lombok中好多功能在Kotlin中已經(jīng)實現(xiàn)了,如果項目暫時還不想使用Kotlin開發(fā),繼續(xù)使用Java的可以嘗試一下。

缺點:

使用lombok雖然能夠省去手動創(chuàng)建代碼的麻煩,但是卻大大降低了源代碼文件的可讀性和完整性,降低了閱讀源代碼的舒適度。

相關(guān)鏈接

Lombok官網(wǎng)

AutoValue相關(guān)

android基礎(chǔ)之依賴注入問題

Lombok的使用和原理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,765評論 25 709
  • 前言 人生苦多,快來 Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,669評論 9 118
  • 摘自:賈童羽 等到了天黑 看到了完美的煙火 又是新的一年 桃花依舊故人已老 是誰的期許 負了情負了你 不是良人不是...
    夜半微眸閱讀 544評論 0 7
  • 很多事情其實沒有自己想象的那么困難,只要你勇敢地邁出第一步。 高考失敗,我剛來這所學(xué)校的時候,第一感覺就是爛、爛、...
    孤名思義閱讀 420評論 0 2
  • 雙峰山,顧名思義,有兩個主峰。山不算高,也就幾百米的樣子,但是植被覆蓋很廣,山下有一個村莊,收入主要靠景區(qū)旅游。我...
    愿河閱讀 441評論 0 1

友情鏈接更多精彩內(nèi)容