使用Lombok简化java代码的编写

Lombok介绍

        我们在开发过程中,通常都会定义大量的JavaBean,然后通过IDE去生成其属性的构造器、getter、setter、equals、hashcode、toString方法,当要对某个属性进行改变时,比如命名、类型等,都需要重新去生成上面提到的这些方法,而且这些方法对业务逻辑,基本上不会有任何的作用。Lombok就是用来简化java的代码开发而生的,通过集成到IDE中,Lombok能够注入开发人员的代码并立即生效。例如,只需将@Data注释添加到数据类中,如下所示,会导致IDE中的一些新方法

github地址:https://github.com/rzwitserloot/lombok

从上图中,我们可以发现,我们就在类上加了一个@Data注解,视图中就自动为我们生成了getter,setter,toString,hashcode,equals以及无参的构造方法。@Data的功能也正式如此。那么怎么来使用Lombok了?

Installation

1、下载Lombok的jar包

下载地址:https://projectlombok.org/download.html

2、运行命令

java -jar lombok.jar

3、指定ide的安装目录


安装程序将尝试检测支持的IDE的位置。 如果无法正确确定安装IDE的位置,则可以手动指定位置。只需点击“安装/更新”,IDE集成即可完成

注意:安装完后需要退出/重启,本人测试直接通过restart IDE,发现无效。

Maven中使用Lombok

在pom文件中加入依赖即可:

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

Lombok的注解

val

可以使用val作为局部变量声明的类型,Lombok将从初始化程序表达式推断该类型。此功能仅适用于局部变量和foreach循环,而不适用于字段。注意:使用val时,该局部变量必须是已经初始化了的。

import java.util.ArrayList;
import java.util.HashMap;
import lombok.val;

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());
		}
	}
}

上面代码对应的java代码如下:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class ValExample {
	public String example() {
		final ArrayList<String> example = new ArrayList<String>();
		example.add("Hello, World!");
		final String foo = example.get(0);
		return foo.toLowerCase();
	}
	
	public void example2() {
		final HashMap<Integer, String> map = new HashMap<Integer, String>();
		map.put(0, "zero");
		map.put(5, "five");
		for (final Map.Entry<Integer, String> entry : map.entrySet()) {
			System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
		}
	}
}

注:此功能目前在NetBeans中暂时不起作用

@Getter and @Setter

@Getter和@Setter注解分别为一个字段生成一个getter和setter方法。getter方法生成遵循布尔属性的惯例,会生成一个isFoo getter方法名称而不是getFoo方法名称。

Lombok annotated code:

@Getter的懒加载:懒加载是一种美德!

import lombok.Getter;

public class GetterLazyExampleBefore {
	@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;
	}
}
对应的java代码如下:

public class GetterLazyExample {
	private final java.util.concurrent.atomic.AtomicReference<java.lang.Object> cached = new java.util.concurrent.atomic.AtomicReference<java.lang.Object>();
	
	public double[] getCached() {
		java.lang.Object value = this.cached.get();
		if (value == null) {
			synchronized(this.cached) {
				value = this.cached.get();
				if (value == null) {
					final double[] actualValue = expensive();
					value = actualValue == null ? this.cached : actualValue;
					this.cached.set(value);
				}
			}
		}
		return (double[])(value == this.cached ? null : value);
	}
	
	private double[] expensive() {
		double[] result = new double[1000000];
		for (int i = 0; i < result.length; i++) {
			result[i] = Math.asin(i);
		}
		return result;
	}
}
建议不要使用懒加载,像这种比较复杂的代码生成,还是自己写!

@NonNull

@NonNull注释用于指示对相应成员进行快速失败空检查的需要。 当放置在Lombok生成setter方法的字段上时,将生成一个null检查,如果被@NonNull注解标注的setter方法的值是null的话,会抛NullPointerException异常。如果该属性在构造方法当中,那么构造方法默认会抛出NullPointerException异常

用法:

@Getter @Setter @NonNull
private List<Person> members;
生成的java代码如下:
@NonNull
private List<Person> members;

public Family(@NonNull final List<Person> members) {
    if (members == null) throw new java.lang.NullPointerException("members");
    this.members = members;
}
    
@NonNull
public List<Person> getMembers() {
    return members;
}

public void setMembers(@NonNull final List<Person> members) {
    if (members == null) throw new java.lang.NullPointerException("members");
    this.members = members;
}

测试代码如下:

@NoArgsConstructor
@AllArgsConstructor
public class Person2 {
	@Setter @Getter @NonNull private String name;
    @Setter @Getter private String ssn;
    @Setter @Getter private String address;
    @Setter @Getter private String city;
    @Setter @Getter private String state;
    @Setter @Getter private String zip;
}

public class Person2Test {
    
    @Test
    public void test(){
        Person2 p = new Person2(null, null, null, null, null, null);
        System.out.println(p);
    }
}
测试结果会抛NullPointerException异常!

@ToString

此注释生成toString方法的实现,默认情况下,任何非静态属性都会包含在方法中。

includeFieldNames属性的用法

@Entity
@Table(name="t_user")
@NoArgsConstructor
@AllArgsConstructor
@ToString(includeFieldNames=false)// 如果值为false,则不会显示属性名称,如果值为true,则会显示属性名称
public class User implements Serializable{

    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	@Id()
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @Setter @Getter private String name;
    @Setter @Getter private String address;
    @Setter @Getter private int age;
    @Setter @Getter private boolean isSuccess;
}
测试代码如下:

public class UserTest {
	
	@Test
	public void test(){
		User user = new User(1, "chhliu", "北京", 25, true);
		System.out.println(user);
	}
}
测试结果如下:

User(1, chhliu, 北京, 25, true)
如果将上面的false改为true,则测试结果如下
User(id=1, name=chhliu, address=北京, age=25, isSuccess=true)

exclude的用法

@NoArgsConstructor
@AllArgsConstructor
@ToString(includeFieldNames=true, exclude={"name", "address"})
public class User implements Serializable{

    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	@Id()
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @Setter @Getter private String name;
    @Setter @Getter private String address;
    @Setter @Getter private int age;
    @Setter @Getter private boolean isSuccess;
}
测试结果如下:

User(id=1, age=25, isSuccess=true)
exclude用来排除不需要显示的属性。

of的用法

@NoArgsConstructor
@AllArgsConstructor
@ToString(includeFieldNames=true, of={"name"})
public class User implements Serializable{

    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	@Id()
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @Setter @Getter private String name;
    @Setter @Getter private String address;
    @Setter @Getter private int age;
    @Setter @Getter private boolean isSuccess;
}
测试结果如下:

User(name=chhliu)
of的作用与exclude的作用相反,包含需要显示的属性。

callSuper的用法

@ToString(callSuper=true,exclude="someExcludedField")
public class Foo extends Bar {
    private boolean someBoolean = true;
    private String someStringField;
    private float someExcludedField;
}
对应生成的java代码如下:

public class Foo extends Bar {
    private boolean someBoolean = true;
    private String someStringField;
    private float someExcludedField;
    
    @java.lang.Override
    public java.lang.String toString() {
        return "Foo(super=" + super.toString() +
            ", someBoolean=" + someBoolean +
            ", someStringField=" + someStringField + ")";
    }
}
会将父类中的属性也显示出来。

@EqualsAndHashCode

此类级别注释将导致Lombok生成equals和hashCode方法,因为这两个方法本身通过hashCode合同绑定在一起。默认情况下,任何不是静态或瞬态的字段都将被两种方法考虑

@EqualsAndHashCode(callSuper=true,exclude={"address","city","state","zip"})
public class Person extends SentientBeing {
    enum Gender { Male, Female }

    @NonNull private String name;
    @NonNull private Gender gender;
    
    private String ssn;
    private String address;
    private String city;
    private String state;
    private String zip;
}
生成的java代码如下:

public class Person extends SentientBeing {
    
    enum Gender {
        /*public static final*/ Male /* = new Gender() */,
        /*public static final*/ Female /* = new Gender() */;
    }
    @NonNull
    private String name;
    @NonNull
    private Gender gender;
    private String ssn;
    private String address;
    private String city;
    private String state;
    private String zip;
    
    @java.lang.Override
    public boolean equals(final java.lang.Object o) {
        if (o == this) return true;
        if (o == null) return false;
        if (o.getClass() != this.getClass()) return false;
        if (!super.equals(o)) return false;
        final Person other = (Person)o;
        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
        if (this.gender == null ? other.gender != null : !this.gender.equals(other.gender)) return false;
        if (this.ssn == null ? other.ssn != null : !this.ssn.equals(other.ssn)) return false;
        return true;
    }
    
    @java.lang.Override
    public int hashCode() {
        final int PRIME = 31;
        int result = 1;
        result = result * PRIME + super.hashCode();
        result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
        result = result * PRIME + (this.gender == null ? 0 : this.gender.hashCode());
        result = result * PRIME + (this.ssn == null ? 0 : this.ssn.hashCode());
        return result;
    }
}
该注解的使用方法和@toString的使用方法类似。

@Data

@Data注释可能是Project Lombok工具集中最常用的注释。它结合了@ToString,@EqualsAndHashCode,@Getter和@Setter的功能。

@Data(staticConstructor="of")
public class Company {
    private final Person founder;
    private String name;
    private List<Person> employees;
}
生成的java代码如下:

public class Company {
    private final Person founder;
    private String name;
    private List<Person> employees;
    
    private Company(final Person founder) {
        this.founder = founder;
    }
    
    public static Company of(final Person founder) {
        return new Company(founder);
    }
    
    public Person getFounder() {
        return founder;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(final String name) {
        this.name = name;
    }
    
    public List<Person> getEmployees() {
        return employees;
    }
    
    public void setEmployees(final List<Person> employees) {
        this.employees = employees;
    }
    
    @java.lang.Override
    public boolean equals(final java.lang.Object o) {
        if (o == this) return true;
        if (o == null) return false;
        if (o.getClass() != this.getClass()) return false;
        final Company other = (Company)o;
        if (this.founder == null ? other.founder != null : !this.founder.equals(other.founder)) return false;
        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
        if (this.employees == null ? other.employees != null : !this.employees.equals(other.employees)) return false;
        return true;
    }
    
    @java.lang.Override
    public int hashCode() {
        final int PRIME = 31;
        int result = 1;
        result = result * PRIME + (this.founder == null ? 0 : this.founder.hashCode());
        result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
        result = result * PRIME + (this.employees == null ? 0 : this.employees.hashCode());
        return result;
    }
    
    @java.lang.Override
    public java.lang.String toString() {
        return "Company(founder=" + founder + ", name=" + name + ", employees=" + employees + ")";
    }
}

@Cleanup

@Cleanup注解可用于确保已分配的资源被释放。 当使用@Cleanup注解本地变量时,任何后续代码都将被包装在try / finally块中,以确保在当前作用域的末尾调用清除方法。默认情况下@Cleanup假定释放资源的方法被命名为“close”,例如ByteArrayOutputStream的关闭流的方法为close,那么就无需指定释放资源的方法,但是如果ByteArrayOutputStream的关闭流的方法叫destroy,那么就需要进行指定了,指定的方式如下:@Cleanup("destroy")

下面我们来验证下这个注解的功能,先覆写资源的释放方法:

public void testCleanup(){
		try {
			@Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream() { // 覆写释放资源方法
				@Override
				public void close() throws IOException {
					super.close();
					System.out.println("I've been closed!");
				}
			};
			baos.write(new byte[] {'Y','e','s'});
			System.out.println(baos.toString());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
测试结果如下:
Yes
I've been closed!
通过上面的测试结果,可以得出,默认调用了close方法来释放资源。

public void testCleanUp() {
    try {
        @Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(new byte[] {'Y','e','s'});
        System.out.println(baos.toString());
    } catch (IOException e) {
        e.printStackTrace();
    }
}
生成的java代码如下:

public void testCleanUp() {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            baos.write(new byte[]{'Y', 'e', 's'});
            System.out.println(baos.toString());
        } finally {
            baos.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Synchronized

在一个实例方法的情况下,或者一个静态方法的类对象,synchronized关键字将锁定当前对象(this)

public void testSynchronized() {
		Runnable runner = new Runnable() {
			public void run() {
				synchronized($lock) {
					System.out.println("Thread locked on $lock and sleeping for 5 seconds.  You should see the date output after the wait.");
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {}
					System.out.println("Done sleeping.");
				}
			}
		};
		new Thread(runner).start();
		
		// Make sure we don't outrun the new thread.
		try {Thread.sleep(500);} catch (InterruptedException e) {}
		
		Date today = new Date();
		System.out.println("Main thread attempting to lock on $lock.");
		System.out.println(synchronizedFormat(today));
	}

private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

@Synchronized // 使用在方法上
    public String synchronizedFormat(Date date) {
        return format.format(date);
    }
// 上面的代码会生成如下的java代码

private final java.lang.Object $lock = new java.lang.Object[0];
private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

public String synchronizedFormat(Date date) {
    synchronized ($lock) {
        return format.format(date);
    }
}
该注解可能会造成一些线程问题,建议慎用!

@NoArgsConstructor and  @AllArgsConstructor

@NoArgsConstructor注解用来生成无参的构造函数

@AllArgsConstructor注解用来生成带参的构造函数(注意:生成的带参构造函数是带有所有的属性)

@Log

目前支持的日志框架如下:


下面以slf4j为例来进行说明

1、pom文件添加依赖

<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.1.2</version>
		</dependency>
2、在src/main/resources目录加入slf4j的配置文件

<?xml version="1.0" encoding="UTF-8"?>  
<configuration>  
     <substitutionProperty name="logbase" value="${user.dir}/logs/ " />  
     <!-- 这个是要配置输出文件的 -->    
     <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">  
         <layout class="ch.qos.logback.classic.PatternLayout">  
              <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern>  
         </layout>  
     </appender>  
     <!-- 文件输出日志 (文件大小策略进行文件输出,超过指定大小对文件备份) -->  
     <appender name="logfile"  
         class="ch.qos.logback.core.rolling.RollingFileAppender">  
         <Encoding>UTF-8</Encoding>  
         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
              <File>${logbase}%d{yyyy-MM-dd}.log.html</File>  
              <FileNamePattern>${logbase}.%d{yyyy-MM-dd}.log.html.zip  
              </FileNamePattern>  
         </rollingPolicy>  
         <triggeringPolicy  
              class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">  
              <MaxFileSize>2MB</MaxFileSize>  
         </triggeringPolicy>  
         <layout class="ch.qos.logback.classic.html.HTMLLayout">  
              <pattern>%date%level%thread%10logger%file%line%msg</pattern>  
         </layout>  
     </appender>  
     <root>  
         <level value="debug" />  
         <appender-ref ref="stdout" />  
         <appender-ref ref="logfile" />  
     </root>  
   
</configuration>
3、测试类

package com.chhliu.lombok.lombokstart;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class LogExampleOther {
	
	public static void main(String... args) {
		log.error("i'm the king of the world");
	}
}
测试结果如下:

2017-05-15 21:15:31,262 [main] ERROR com.chhliu.lombok.lombokstart.LogExampleOther - i'm the king of the world

从上面的示例中,可以发现,@Log的功能还是非常方便的。Lombok还提供了一个简易的日志浏览界面,效果如下:



@Builder

这是我最喜欢的一个注解了,可以很方便的将实体类转换成链式api的调用方式,示例如下:
@Builder
public class BuilderExampleBefore {
	private String name;
	private int age;
}

测试类如下:
public class BuilderExampleBeforeTest {
	
	@Test
	public void test(){
		BuilderExampleBuilder builder = BuilderExample.builder().age(20).name("chhliu");// 链式api的方式
		System.out.println(builder.toString());
	}
}
从上面的示例中可以看到,我们使用了链式api来实现赋值,比构造方法一个个的set要好看许多吧!

总结

Lombok到目前为止,存在很多的争议,具体技术选型的时候,要看项目的侧重点!

官网地址如下:http://jnb.ociweb.com/jnb/jnbJan2010.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值