为啥我要重载方法?

Posted

技术标签:

【中文标题】为啥我要重载方法?【英文标题】:Why should I ever overload methods?为什么我要重载方法? 【发布时间】:2016-11-27 00:00:42 【问题描述】:

我在我的重载方法一书中找到了两个例子,但并没有清楚地解释它为什么有用:

package keepo;
public class Main
    public static void main(String [] args)
    
        int newScore = calculateScore("Tim", 500);
        System.out.println("New Score is" + newScore);
        calculateScore(75);

    
    public static int calculateScore(String playerName, int score)
        System.out.println("Player" + playerName +"Has score" + score);
        return score * 1000;
    
    public static int calculateScore(int score)
        System.out.println("Unnamed player scored" + score + "points");
        return score * 1000;
    

这很简单,但老实说,这里的方法重载似乎没什么用,而且似乎只是为了这样做而这样做。

本书中的下一个示例执行方法重载,这似乎更有用,因为该程序将英尺计算为厘米,并且有一种方法可以输入英尺和英寸,还有一种方法可以输入英寸。但是,为此创建两个单独的方法似乎仍然很容易。

话虽如此,这样做有什么真正的好处吗? (我读了this,但我并不是很满意。制作新方法似乎同样容易。)

【问题讨论】:

查看InputStream.read 的一些示例...或PrinterWriter.println 这是“不同的方法”,它只是一种工具,因此您不必为它们发明独特的名称。例如 String.valueOf(int) 和 String.valueOf(float) 看起来像 String.valueOfInt(int) 和 String.valueOfFloat(float) - 这只是程序员发明和记住名称的不需要的工作。 给定的例子有点做作。现实世界的场景更有可能为每个玩家分配一个默认名称,因为玩家“未命名”的可能性很混乱。然而,现实世界中有很多很好的重载示例。一个很好的例子可能是list.add(e),它基本上等同于list.add(list.size(), e)。大多数时候我们使用前者,但有时我们需要指定一个索引。 Why to use Interfaces, Multiple Inheritance vs Interfaces, Benefits of Interfaces?的可能重复 【参考方案1】:

我认为,如果您谈论函数/方法重载的真正好处,如果没有这些好处,您将无法解决问题,那么正如您在问题中指出的那样,您将找不到任何东西。

但是它有什么帮助呢?让我们考虑这个例子。

假设我正在创建一个应用程序,通过他的名字找到一个人,并且我声明并定义了一个方法

public Person[] findPerson(String name)

现在我们得到一个要求,我们必须通过他的出生日期找到一个人,所以引入一个新方法

public Person[] findPerson_byDOB(Date date)

假设这种情况继续下去,我们的应用程序中有这么多方法。

public Person[] findPerson(String name)
public Person[] findPerson_byDOB(Date date)
public Person[] findPerson_byAddress(Address address)
public Person[] findPerson_bySSN(SSN ssn)
public Person[] findPerson_byDepartment(Department department)
public Person[] findPerson_byMobile(Mobile mobile)

这只是一部分;当我们被要求引入多个参数时,这可以继续进行,例如

public Person[] findPerson_byMobileAndAddress(Mobile mobile, Address address)
public Person[] findPerson_byAddressAndDepartment(Address address, Department department)
public Person[] findPerson_byDOBAndDepartment(DOB dob, Department, department)
public Person[] findPerson_byAddressAndDOB(Address address, DOB dob)

还有更多...

虽然这看起来有点夸张,但请相信我,在进行实际的行业级应用程序时,我们可能会遇到这样的情况:我们得到了成百上千种这样的方法,最终我们需要一个目录这些方法的实际作用。

当我们不得不使用所有这些方法的名称时,这实际上是一场噩梦。

但是,当所有参数不同时,我们可以给函数起相同的名字,这样真的很容易记住。

public Person[] findPerson(String name)
public Person[] findPerson(Date date)
public Person[] findPerson(Address address)
public Person[] findPerson(SSN ssn)
public Person[] findPerson(Department department)
public Person[] findPerson(Mobile mobile)
public Person[] findPerson(Mobile mobile, Address address)
public Person[] findPerson(Address address, Department department)
public Person[] findPerson(DOB dob, Department, department)
public Person[] findPerson(Address address, DOB dob)

现在正如大卫在his answer 中指出的那样,我们都知道如何获取String 的整数值;可能我们在某处读过它。

static String.valueOf(new Integer(1));

但是你知道还有多少同名重载的方法吗?

static String.valueOf(boolean b)
static String.valueOf(char c)
static String.valueOf(char[] data)
static String.valueOf(double d)
static String.valueOf(float f)
static String.valueOf(int i)
static String.valueOf(long l)
static String.valueOf(Object obj)

好处是您不必全部记住它们。您甚至不必猜测,因为它一直是同一个名字。


编辑 按照 Namnodorel 的建议

考虑PrintStream 类的这个重载方法。

void println()
void println(boolean x)
void println(char x)
void println(char[] x)
void println(double x)
void println(float x)
void println(int x)
void println(long x)
void println(Object x)
void println(String x)

如果我们不得不写,请考虑一下可读性:

void println_emptyLine()
void println_boolean(boolean x)
void println_character(char x)
void println_characterArray(char[] x)
void println_double(double x)
void println_float(float x)
void println_integer(int x)
void println_long(long x)
void println_object(Object x)
void println_string(String x)

【讨论】:

另一个常见的例子是 System.out.println() 太棒了!所以我想它更像是一种组织和清晰的工具? 是的,我们可以这么说! public Person[] findPerson(Mobile mobile) 在那里两次;那将是一个编译错误,对吧? @Izkata - 你知道,本可以建议编辑而不是发表评论。现在这样做了 - 这是因为 findPerson_byMobileAndAddress 错误地仅将 Mobile 作为参数。我怀疑当@RamanSahasi 将该方法“扁平化”为刚刚滑入的重载版本时。嘿-我实际上将另一个观点视为支持重载-非重载签名是错误的并且它完全溜走了。 【参考方案2】:

当您使用不同类型的值创建执行相同操作的方法时,重载很有用。

Math 类提供了一个完美的例子——它有按类型划分的函数重载组——四个abs、四个min、四个max,等等:

int max(int a, int b) ...
float max(float a, float b) ...
long max(long a, long b) ...
double max(double a, double b) ...

没有重载的替代方法会强制您将类型“编码”为方法的名称,例如Math.intMax(a, b) 不利于用户代码的可读性。

【讨论】:

@cresjoy 见Access Modifiers 额外的好处是:假设您编写了自己的类,出于某种原因,定义该类的最大值是有意义的。由于重载,您现在可以只写myObject max(myObject a, myObject b),而不必为max 取一个新名称。在没有重载的语言中,就像“哎呀,那个名字已经被占用了”。【参考方案3】:

简单明了: 重载只是 Java (但大多数现代和灵活的语言都使用它)(以及其他语言,如 C++ 或 C#)提供的一种可能性,以允许开发人员为相同的方法/函数名称创建几个其中。

为什么? 因为方法命名很重要,方法命名应该传达它的行为。 因此,如果两个方法具有相同的行为(例如转换为字符串) 但是一个使用 long 作为输入,而另一个使用 int 作为输入,为什么要使用不同的方法名称?

    String myString = String.valueOf(new Integer(1));
    String myOtherString = String.valueOf(new Long(2));

意图和目标是一样的,只是输入的参数变了。

当重载有意义时​​,你应该使用它而不是创建一些变体笨拙的名称。

【讨论】:

“大多数现代和灵活的语言都使用它”——需要引用。一些不提供方法重载的流行语言包括 Python、Ruby、Rust、Haskell、OCaml……这些语言通常提供其他解决方案,例如命名参数,以解决不需要依赖静态调度的底层问题。 (快速:如果f 是纯的x == yf(x) != f(y) 在Java 中怎么可能?Solution.) 抱歉,输入时不要换档 :) @wchargin 你是对的。我说了一件愚蠢的事 :) 我指的是我使用过的语言(C++、Java、C#)。关于您的示例,这是一种预期行为,因为纯函数概念对 Java 不友好,因为 Java 不依赖于函数式编程概念。 @wchargin,我已更正。谢谢你的精确:)【参考方案4】:

方法重载在以下场景中很有用:

假设您有一个跟踪名称列表的类。你有上面的代码风格,每个方法都有自己的方法来执行这个列表的操作。

列表的内部表示突然发生了变化(可能从array 变为ArrayList,这并不重要)。你想成为负责重构每个的人吗? 。方法?

方法重载很有用,因为这样您就可以通过单个通用方法路由所有操作。这意味着每当内部表示发生变化时,您只需更改该通用方法,所有其他专用方法仍然可以正常工作。


另外,请考虑您提供的示例。如果您想更改程序打印消息的方式会发生什么?您必须修改这两种方法以打印相同类型的消息,这是维护的噩梦。当然,现在看起来很小,但是想想当你的项目增长时,你就会开始有更多的方法依赖于这种(有效固定的)消息格式。

【讨论】:

所以在第一种方法中打印一条消息,在第二种方法中也是如此。如果您想标准化打印这些消息的方式,每次更改方法 1 的打印方式时,都必须更改方法 2 的打印方式,除非您想保留方法 2 的旧方式。尝试维护会很麻烦许多方法,这就是为什么在这种情况下重载是好的。 但我本可以将我在第二个或第一个方法中写的所有内容创建另一个方法,只需在方法名称的末尾添加一个 s,然后使用该名称调用它,什么都不会改变.【参考方案5】:

我是一个对象,我有一个能力,能力是固定的,但可以接受各种参数。

如果能力可以接受1000种参数,你是不是要绞尽脑汁想1000个能力名称?

将其他人的帖子视为重载的良好做法,并将 JNIEnv 的做法视为不好的做法,因为 C 不支持重载。

CallStaticObjectMethod,
CallStaticObjectMethodV,
CallStaticObjectMethodA,
CallStaticBooleanMethod,
CallStaticBooleanMethodV,
CallStaticBooleanMethodA,
CallStaticByteMethod,
CallStaticByteMethodV,
CallStaticByteMethodA,
CallStaticCharMethod,
CallStaticCharMethodV,
CallStaticCharMethodA,
CallStaticShortMethod,
CallStaticShortMethodV,
CallStaticShortMethodA,
CallStaticIntMethod,
CallStaticIntMethodV,
CallStaticIntMethodA,
CallStaticLongMethod,
CallStaticLongMethodV,
CallStaticLongMethodA,
CallStaticFloatMethod,
CallStaticFloatMethodV,
CallStaticFloatMethodA,
CallStaticDoubleMethod,
CallStaticDoubleMethodV,
CallStaticDoubleMethodA,
CallStaticVoidMethod,
CallStaticVoidMethodV,
CallStaticVoidMethodA,

登录JNI了解更详细的结构定义

【讨论】:

several of your methods might do similar things but one will take 4 parameters and one will take three - 所以?如果您打电话说add(5, 7, 56)add(5, 7, 56, 1),则期望它们都会返回给您所有的总和。我没有一个理由想出你想要add4things(int, int, int, int)add3things(int, int, int) 方法的地方。所有重载的方法都应该做同样的事情,但方式略有不同。没有混乱。该方法的签名是它所拥有的合同的一部分,因此getAge(Tree tree)getAge(Frog frog) 是明确的。【参考方案6】:

重载的另一个原因是提供一个或多个默认参数。

考虑以下几点:

class Something 
    // Imagine that this is a real class, that does real work.

    public void doSomething(boolean b, char c, double d, int i) 
        // Imagine that this is one of the main components of class Something.
        System.out.println("Hi. You passed: " + b + ", " + c + ", " + d + ", and " + i + ".");
    

    public void doSomething(boolean b, char c, double d) 
        doSomething(b, c, d, 42);
    

    public void doSomething(boolean b, char c) 
        doSomething(b, c, 1.3579);
    

    public void doSomething(boolean b) 
        doSomething(b, 'q');
    

    public void doSomething() 
        doSomething(true);
    

在此示例中,每个重载都为其中一个参数提供了一个默认值,将它们链接在一起,直到您获得对实际工作的 doSomething() 版本的完整调用。

Something s = new Something();
Something t = new Something();
Something u = new Something();

// ...

s.doSomething(true, 'c', 2.9);
t.doSomething(false, 'z');
u.doSomething();

有关示例,请参阅 here。

【讨论】:

以上是关于为啥我要重载方法?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这行得通?方法重载+方法覆盖+多态

为啥 ruby​​ 不支持方法重载?

为啥没有为不同的返回类型定义方法重载?

为啥 WCF 中不允许方法重载?

为啥java中需要方法重载和覆盖? [复制]

为啥我的方法“DropDownListFor”没有重载?