【effective Java读书笔记】方法(二)
前言:
越发喜欢写读书笔记了,写完之后,后续的工作中,平时写代码,就会比较深刻的记起来,“原来这个地方可以这么用”。
《第40条至41条》
正文:
一、谨慎设计方法签名,其中值得一提的是:避免过长的参数列表。
对了,话说方法签名应该很熟悉吧:参数类型、参数个数、参数顺序。
避免过长的参数列表的三种方法:
1、方法分解成多个方法;
书中举例:在子列表中查找元素的第一个索引和最后一个索引。(java.util.List接口)
public class ListApply {
public static void main(String[] args) {
List<String> strs = new ArrayList<>();
// TODO 提供数据
for (int i = 0; i < 20; i++) {
strs.add("test"+i);
}
List<String> subStrs= strs.subList(3, 13);
System.out.println(subStrs.indexOf(subStrs.get(0)));
System.out.println(subStrs.lastIndexOf(subStrs.get(subStrs.size()-1)));
}
}
执行结果:
0
9
原本提供这样一个方法需要三个参数,子列表、子列表第一个索引、子列表的最后一个索引。
第8行,List接口提供subList方法,获得子列表;
第9行,根据
subStrs.get(0)
获取第一个对象,通过indexOf方法获得第一个对象出现的第一次的index;
第10行,根据
subStrs.get(subStrs.size()-1))
获取最后一个对象,通过lastIndexOf获取最后一个对象出现的最后一次的index;
2、创建辅助类;
辅助类,顾名思义,就是用来作为辅助工具的类。
举个例子:
public class HelpClass {
public static class Help{
public static int add(int x,int y){
return x+y;
};
public static int min(int x,int y){
return x-y;
};
}
public static void main(String[] args) {
int x =3;
int y =2;
System.out.println("3+2=?");
System.out.println(HelpClass.Help.add(x, y));
System.out.println("3-2=?");
System.out.println(HelpClass.Help.min(x, y));
}
}
执行结果:
3+2=?
5
3-2=?
1
其中:
Help就是辅助类,原本一个这样的计算,需要传递三个入参:第一个数,第二个数,算数操作符。
现在添加辅助类后,只需要添加两个入参。(以上代码用枚举写更佳)
public class HelpClass {
public static enum Opre{
ADD("+"){
@Override
public int apply(int x, int y) {
return x+y;
}
},
MIN("-"){
@Override
public int apply(int x, int y) {
return x-y;
}
};
public final String oper;
Opre(String oper) {
this.oper = oper;
}
public abstract int apply(int x,int y);
}
public static void main(String[] args) {
int x =3;
int y =2;
System.out.println("3+2=?");
System.out.println(HelpClass.Opre.ADD.apply(x, y));
System.out.println("3-2=?");
System.out.println(HelpClass.Opre.MIN.apply(x, y));
}
}
3、从对象构建到方法调用都采用Build模式。
直接举例:
public class User {
private String name;
private String addr;
private int age;
public String getName() {
return name;
}
public String getAddr() {
return addr;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "User [name=" + name + ", addr=" + addr + ", age=" + age + "]";
}
public static class Builder{
private User user = new User();
public Builder setName(String name){
user.name = name;
return this;
}
public Builder setAddr(String addr){
user.addr = addr;
return this;
}
public Builder setAge(int age){
user.age = age;
return this;
}
public User build(){
return user;
}
}
}
使用如下:
public class Test {
@org.junit.Test
public void test() {
Builder builder = new Builder();
User user =builder.setName("bear").setAge(14).setAddr("beijing").build();
System.out.println(user);
}
}
执行结果如下:
User [name=bear, addr=beijing, age=14]
当然这个方法显著的减少了参数。使用起来也比较方便。
二、慎用重载
关键点出来的就是重载和覆盖的区别:书中提及“对于重载方法的选择是静态的,对于覆盖方法的选择是动态的”。
1、先看一个书中重载的例子:
public class OverRideTest {
public static String classify(Set<?> s){
return "Set";
}
public static String classify(List<?> lst){
return "List";
}
public static String classify(Collection<?> c){
return "Collection";
}
public static void main(String[] args) {
Collection<?>[] collections = {new HashSet<String>(),new ArrayList<String>(),new HashMap<String,String>().values()};
for (Collection<?> c:collections) {
System.out.println(classify(c));
}
}
}
乍看之下,有点懵!这结果是不是应该Set,List,Collection;
而实际结果呢?
Collection
Collection
Collection
原因其实很简单,重载嘛,挑选最合适的。
public static String classify(Collection<?> c){
return "Collection";
}
而且重载方法调用是在编译时决定的。那么一旦选定就不能改了。无异议是否?
2、再看一个覆盖的例子:
代码如下
public class Test2 {
public static void main(String[] args) {
Wine[] wines = { new Wine(), new RedWine(), new YellowWine() };
for (Wine wine : wines) {
System.out.println(wine.name());
}
}
}
class Wine {
String name() {
return "wine";
}
}
class RedWine extends Wine {
String name() {
return "RedWine";
}
}
class YellowWine extends Wine {
String name() {
return "YellowWine";
}
}
执行结果:
wine
RedWine
YellowWine
3、慎用重载的例子:
书中这个例子显得很是经典:
public class SetList {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<>();
List<Integer> list = new ArrayList<Integer>();
for (int i = -3; i < 3; i++) {
set.add(i);
list.add(i);
}
for (int i = 0; i <3; i++) {
set.remove(i);
list.remove(i);
}
System.out.println(set + "" + list);
}
}
看上去以为结果会是
[-3, -2, -1]
[-3, -2, -1]
实际结果却是:
[-3, -2, -1][-2, 0, 2]
原因是什么?第一个Set与我们预期相同。Set添加和删除都会先装箱,再添加删除。
第二个List添加时装箱,但是删除的时候重载了,导致remove第几个位置的,而不是删除某个指定的对象。