java重载(Overload)

问题

  • 一直以来,重载(Overload)都做为java语言的一项重要语言特性介绍
  • 需要重载的情况:
    • 方法需要处理多个类型的参数,为每种类型生成一个重载的方法
    • 方法有多个参数,有些参数可不传或者有默认值,为每种类型生成一个重载的方法
    • 类的初始化需要多个参数,有些参数可不传或者有默认值,为每种类型生成一个重载的构造方法
  • 优点:
    • 如果重载的各个方法实现的逻辑不一样,则方法重载体现了java中对象的多态性
  • 缺点:
    • 如果重载的各个方法实现的逻辑完全一样,只是为了适应有些参数可不传或者有默认值的情况,则重载会产生大量重复代码
  • 本文重点讨论:重载的各个方法实现的逻辑完全一样,为了适应有些参数可不传或者有默认值,重载会产生大量重复代码的问题

相关概念

java重载的相关定义

java方法签名

  • 方法的名字和參数列表构成java的方法签名
  • 方法签名不包含方法的返回类型
  • 方法签名唯一确定一个方法

java方法重载

  • 在一个类里面,方法名字相同,参数类型列表不相同,返回类型可以相同也可以不相同,称为方法重载
  • 方法重载,两个方法方法签名不相同
  • 方法重写,两个方法方法签名相同
  • 重载规则:
    • 被重载的方法必须改变参数列表(参数个数或类型不一样)
    • 被重载的方法可以改变返回类型
    • 被重载的方法可以改变访问修饰符
    • 被重载的方法可以声明新的或更广的检查异常
    • 方法能够在同一个类中或者在一个子类中被重载
    • 无法以返回值类型作为重载函数的区分标准
  • 方法重载是java中类的多态性表现

java的可变参数

  • 使用…表示可变参数,调用时可以传入多个参数
  • 只能出现在参数列表的最后
  • 在方法中以数组的形式访问
  • 使用"Object… args"可变参数可减少上述的问题,也存在问题:
    • 参数类型检查会失效,并且大大降低代码的可读性,丧失静态语言的优势
    • 在方法内部访问时,需要判断参数数组的长度以确认用户是否传入些参数
    • 个人理解可变参数适用于类型一样,但个数为0-n的参数

建造者模式

  • 主要解决软件系统中"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常变化,但组合的算法却比较稳定
  • 如果一个对象的参数很多,如果全部写在构造方法中,会使使用者难以理解,可以使用建造者模式进行简化。当然也可使用构造方法重载,但是重载的构造方法会非常多
//**使用者不必了解所有的参数之后才可调用,只需关注自己使用的参数
return RefreshTokenJwtVerifier.Builder.newBuilder(redisTemplate)
                .accessTokenExpireTime(accessTokenExpireTime)
                .refreshTokenExpireTime(refreshTokenExpireTime)
                .secret(secret)
                .build();
  • 使用建造者模式可以解决类的初始化需要多个参数,有些参数可不传或者有默认值,为每种类型生成一个重载的构造方法的问题,但是相应生成额外的代码

javascript和python的相关定义

方法签名

  • 方法名称即为方法签名,方法名唯一确定一个方法

方法重载

  • 没有方法重载的概念,在一个类如果存在名称相同的两个方法,前面的方法被重写,只有最后一个生效
  • python通过动态类型,默认参数,可变参数(*,**),关键字参数等语言特性保证一个方法名称可适配多种类型的参数列表
  • javascript可使用动态类型,默认参数,扩展运算符(…)。甚至可省略参数列表,传入任意参数,然后使用内部变量arguments获取所有参数

可变参数

实例

遇到的问题

/**request方法有多个参数,其中只有url的method是必须的,params可不传,hasHeader默认为true
为了调用方便,写了4个重载的方法,造成了大量重复代码*/
public class Test {

    @Autowired
    private TestRestTemplate restTemplate;
    protected String accessToken = "test";
    public static String ACCESS_TOKEN_KEY = "Authorization";

    protected ResponseEntity request(String url, HttpMethod method){
        return this.request(url, method, null, true);
    }

    protected ResponseEntity request(String url, HttpMethod method, boolean hasHeader){
        return this.request(url, method, null, hasHeader);
    }

    protected ResponseEntity request(String url, HttpMethod method, MultiValueMap params){
        return this.request(url, method, params, true);
    }

    /**最终的逻辑代码,如果只写这一个方法,调用不方便:request(url, GET, null, true)
    重载后的调用:request(url, GET)*/
    protected ResponseEntity request(String url, HttpMethod method, MultiValueMap params, boolean hasHeader){
        HttpEntity> entity = hasHeader ? new HttpEntity<>(params, this.getHeaders()) : new HttpEntity<>(params);

        return restTemplate.exchange(url, method, entity, ApiResponse.class);
    }

    private MultiValueMap getHeaders(){ ... }
}

java的解决方案

//**1.可变参数
public class Test {

    @Autowired
    private TestRestTemplate restTemplate;
    protected String accessToken = "test";
    public static String ACCESS_TOKEN_KEY = "Authorization";

    //**参数类型检查会失效,可读性极差
    //**在方法内部,需要一些额外的处理逻辑
    //**可变参数适用于类型一样,但个数为0-n的参数
    //**调用方式:request(url, GET)
    protected ResponseEntity request(String url, HttpMethod method, Object... args){
        MultiValueMap params = null;
        boolean hasHeader = true;

        for(Object arg : args){
            if(arg instanceof MultiValueMap) params = (MultiValueMap)arg;
            else if(arg instanceof Boolean) hasHeader = (Boolean)arg;
        }

        HttpEntity> entity = hasHeader ? new HttpEntity<>(params, this.getHeaders()) : new HttpEntity<>(params);
        return restTemplate.exchange(url, method, entity, ApiResponse.class);
    }

    private MultiValueMap getHeaders(){ ... }
}

//**2.如果是参数为成员变量,则可使用建造者模式
public class Test {

    @Autowired
    private TestRestTemplate restTemplate;
    protected String accessToken = "test";
    public static String ACCESS_TOKEN_KEY = "Authorization";

    private String url;
    private HttpMethod method;
    private MultiValueMap params;
    private boolean hasHeader = true;

    private Test(){}

    //**调用方式:Test.Builder.newBuilder().build(url, GET).request();
    protected ResponseEntity request(){
        HttpEntity> entity = hasHeader ? new HttpEntity<>(params, this.getHeaders()) : new HttpEntity<>(params);

        return restTemplate.exchange(url, method, entity, ApiResponse.class);
    }

    //**需要生成多余的代码Builder类
    //**如果可选参数很多,建造者模式是一个非常好的方法
    public static class Builder {
        private Test test;

        private Builder() {}

        public static Builder newBuilder() {
            Builder builder = new Builder();
            builder.test = new Test();
            return builder;
        }

        public Builder params(MultiValueMap params) {
            this.test.params = params;
            return this;
        }

        public Builder hasHeader(boolean hasHeader) {
            this.test.hasHeader = hasHeader;
            return this;
        }

        public Test build(String url, HttpMethod method){
            this.test.url = url;
            this.test.method = method;

            return this.test;
        }
    }

    private MultiValueMap getHeaders(){ ... }
}

javascript的解决方案

//**javascript函数任何参数都可不传,如果没有设置默认值,则默认为null
class Test {

    constructor (){
        this.accessToken = "test";
        this.ACCESS_TOKEN_KEY = "Authorization";
    }
    
    //**写法1:调用request(url, GET)
    //**如果params不需要,但hasHeader为false,调用request(url, GET, null, false)
    request(url, method, params, hasHeader = true){
        let entity = hasHeader ? new HttpEntity(params, this.getHeaders()) : new HttpEntity(params);

        return restTemplate.exchange(url, method, entity, ApiResponse.class);
    }

    //**写法2:调用request(url, GET)
    //**如果params不需要,但hasHeader为false,调用request(url, GET, null, false)
    //**本函数可接收任何参数,所以javascript不存在函数重载
    request(){
        let [url, method, params, hasHeader] = arguments
        hasHeader = hasHeader == null ? true : hasHeade
        let entity = hasHeader ? new HttpEntity(params, this.getHeaders()) : new HttpEntity(params);

        return restTemplate.exchange(url, method, entity, ApiResponse.class);
    }
    
    //**javascript的扩展运算符(...),在这里把所有参数合并成args数组
    def request(...args):
        pass
    
    getHeaders() { ... }
}

python的解决方案

//**python函数参数默认参数可不传,值为设置的默认值
//**python支持关键字参数,可直接指定参数名=值,如request(hasHeader=false)
class Test :

    ACCESS_TOKEN_KEY = "Authorization"
    
    
    def __init__(self):
         self.accessToken = "test"
    

    //**调用request(url, GET)
    //**如果params不需要,但hasHeader为false,调用request(url, GET, hasHeader=false)
    def request(self, url, method, params=None, hasHeader=True):
        entity = HttpEntity(params, this.getHeaders()) if hasHeader  else HttpEntity(params)

        return restTemplate.exchange(url, method, entity, ApiResponse.class)
        
      
    //***args1接收所有普通参数,args2接收所有的关键字参数
    //**本函数可接收任何参数,所以python不存在函数重载
    def request(*args1 **args2):
        pass


    def getHeaders(self):
        pass

总结

  • java
    • 没有找到比较合适的替代方法,为了方便调用,本人还是继续使用重载
  • javascript
    • 大部分情况下,都能够以比较简化的方式调用
    • 函数不能重载,所以如果params不需要,但hasHeader为false,只能使用request(url, GET, null, false)方式调用
  • python
    • 默认参数和关键字参数处理此类问题非常方便,是比较理解的处理方法
    • 在构造方法有多个参数时,也可以使用此方法,调用时和java的建造者模式一样,只需关心自己使用的参数

你可能感兴趣的:(java8,python,javascript)