Ruby Ruby 不同入参类型的函数,能不能用同一个名字

tomanderson · 2021年01月07日 · 最后由 lyb124553153 回复于 2021年01月11日 · 442 次阅读

java 等静态语言中,我可以用两个相同名称的函数来处理不同类型的入参:

//处理str
public static String handle(String str){
    ……
}

//处理数组
public static List<String> handle(List<String> arr) {
    //代码仅为示意
    for(String str : arr){
        handle(str)
    }
}

当我调用 handle 时,无论什么类型的入参都可以用,很方便。

在 ruby 中,我只能用两个不同名称的函数:

def handle_str(str)

def handle_arr(arr)

我也想过在同一个函数中通过判断入参类型来分别处理,但是感觉还是很麻烦,特别是当 handle_arr 调用了 handle_str 的时候。

有没有更优雅的方式呢?

ruby 不支持 overload。

你举得例子,简单的情况下能写出这样。不过复杂的情况需要具体分析。

def handle(any_type_data)
  Array(any_type_data).each do |e|
    do_someting
  end
end

或许这样吧,之前经常见到的做法

def handle(value)
  if value.is_a?(String)
    # do_someting
  else if value.is_a?(Array)
    # do_someting
  end
end

就跟 java 的 instanceof 一样判断

ruby 不支持

参数重载功能,一般是静态强类型语言才会提供的功能,ruby 作为动态强类型语言,采用这种方式会比较怪异,和 ruby 的风格不搭,而且要做种模式,ruby 先要把类型标注做好才行。

sorbet 用起来

转换一下思路,用这种方法是不是也行?

def handle(options={})
  str = options.fetch(:str, "hello world")
  list = options.fetch(:list, [])

  return str unless str.nil?
  return list..map {|e| e += 1} unless list.nil?
end

handle(str: 'hello ruby')
handle(list: [1,2,3])

ruby 不是 java

楼主能问出这种问题,说明现在静态语言占上风了,在 10 年前问的比较多的是 “java 的一个方法如何能像动态语言一样接受可变参数?”

handle(*args) 不好吗?

ruby 好像很少直接处理数组,而是使用map

就像楼上说的,一般直接用is_a?判断就行了,像你说的handle_arr调用了handle_str的那种情况,也不冲突,看上去就像是:

def handle(obj)
  if obj.is_a? arr
    handle_arr(obj)
  elsif obj.is_a? str
    handle_str(obj)
  else
    # do_something
  end
end

def handle_arr(list)
  list.map { |item| handle_str(item) }
end

def handle_str(str)
  # do_something
end

有些情况下,用respond_to?可以获得更好的扩展性,也就是鸭子类型的用法,这里用一个别的例子:

def handle(file)
  filename = if file.repond_to? :to_path
               file.to_path
             elsif file.is_a? String
               file
             else
               # log and raise type not suported exception
             end

  File.open(file, 'w')
  # do_something
end

这样不仅可以处理File类型的对象,还可以处理其他带有to_path方法的对象。

当然最理想的情况是直接让参数自己做处理,也就是人们经常说的 Tell, Don't Ask ,比如说

class StringLike
  def handle
    # do_something
  end
end

class ArrayLike
  def handle
    # do_something
  end
end

def handle(obj)
  obj.handle
end

array_like = ArrayLike.new
string_like = StringLike.new

handle(array_like)
handle(string_like)

扯远了,说起类型检测就条件反射了

😏

ruby 在类上加方法很容易 可以在 array 和 string 各自定义一个 handle 方法

运行的时候就不是 handle(obj), 而是 obj.handle

这样就更容易实现函数式编程

需要 登陆 后方可回复, 如果你还没有账号请 注册新账号