52

我经常发现自己从类中提取常见行为到只包含一组静态方法的帮助程序/实用程序类中。我经常想知道是否应该将这些类声明为抽象类,因为我真的想不出实例化这些类的正当理由?

将这样的类声明为抽象类的优点和缺点是什么。

public [abstract] class Utilities{

   public static String getSomeData(){
       return "someData";
   }

   public static void doSomethingToObject(Object arg0){
   }
}
4

11 回答 11

83

您可以只声明一个什么都不做的私有构造函数。

将类声明为“抽象”的问题在于,abstract 关键字通常意味着该类旨在被子类化和扩展。这绝对不是你想要的。

于 2008-11-21T17:32:27.590 回答
18

不要费心将它们抽象化,而是包含一个私有的无参数构造函数以防止它们被实例化。

感兴趣的人的比较点:在 C# 中,您可以将类声明为静态,使其以编译形式抽象密封(Java 的最终形式),并且根本没有任何实例构造函数。这也使得声明该类型的参数、变量、数组等成为编译时错误。便利。

于 2008-11-21T17:33:14.127 回答
12

我没有将实用程序类声明为抽象的,而是将它们声明为最终的并将构造函数设为私有。这样它们就不能被子类化,也不能被实例化。



public final class Utility
{
    private Utility(){}

    public static void doSomethingUseful()
    {
        ...
    }
}
于 2008-11-22T17:51:38.130 回答
4

我会在私有构造函数之外添加更多步骤:

public class Foo {
   // non-instantiable class
   private Foo() { throw new AssertionError(); }
}

抛出AssertionError阻止同一类中的方法实例化该类(好吧,他们可以尝试)。这通常不是问题,但在团队环境中,你永远不知道有人会做什么。

关于“abstract”关键字,我注意到在许多实例中都包含实用程序类:

public class CoreUtils { ... }
public class WebUtils extends CoreUtils { ... }

public class Foo { ... WebUtils.someMethodInCoreUtils() ... }

我相信这样做是为了让人们不必记住要包含哪个实用程序类。这有什么缺点吗?这是反模式吗?

问候, LES

于 2009-01-29T20:35:27.093 回答
3

通过将它们声明为抽象,您实际上是在向其他编码人员表明您打算派生这些类。真的,你是对的,没有太大区别,但这里的语义实际上更多的是关于其他人查看你的代码的解释。

于 2008-11-21T17:31:55.580 回答
2

正如其他人所说,制作一个私有的无参数构造函数。除了类本身之外,没有人可以创建它的实例。

正如其他人已经展示了它是如何用其他语言完成的,下面是你如何在下一个 C++ 版本中做到这一点,如何使一个类不可实例化:

struct Utility { 
    static void doSomething() { /* ... */ } 
    Utility() = delete; 
};
于 2008-11-22T16:56:17.150 回答
2

我认为最好使用私有的无参数构造函数来声明实用程序类 final。此外,此类的所有成员都应该是静态的。

在一个语句中完成所有这些操作的一种简单方法是使用@UtilityClassLombok 的注释

@UtilityClass
public class Utilities{

   public String getSomeData() {
       return "someData";
   }

   public void doSomethingToObject(Object arg0) {
   }
}

如果您使用@UtilityClass注解,您可以跳过上面示例中的静态关键字,因为 Lombok 在编译期间会自动添加它们。

于 2019-08-21T13:52:21.463 回答
0

不,但是如果您的语言支持它,则有一个强有力的论据,即在大多数情况下它们应该(可以)被声明为“静态”......静态告诉编译器它们不能被实例化,并且它们中的所有方法必须是静态的。

摘要适用于具有基于实例的实现细节的类,派生类的实例将使用这些细节......

于 2008-11-21T17:31:57.890 回答
0

Helper / Utility 方法很好。不必担心将它们添加到应用程序或框架内的库中。我见过的大多数框架都以多种方式使用它们。

话虽如此,如果您想真正了解它们,您应该研究C# 3.0 中的扩展方法。使用扩展方法将使您的实用程序更像是您的框架的“整体”部分,这看起来就像您通过考虑使它们抽象来尝试做的事情。更不用说扩展方法写起来很有趣!

于 2008-11-21T17:41:28.960 回答
0

有人提到,在 C# 3.0 中,您可以通过扩展方法来实现这一点。我不是 C# 人,在 1.5/2.0 的日子里做过一些,但从那以后就没有使用过。基于一个非常粗略的理解,我认为可以在 java 中使用静态导入来完成类似的事情。我意识到这根本不是一回事,但如果目标是让这些实用方法对调用类看起来更“原生”(因为没有更好的术语),我认为它会成功。假设我在原始问题中声明了 Utilities 类。

import static Utilities.getSomeData;

public class Consumer {

    public void doSomething(){

        String data =  getSomeData();
    }

}
于 2008-11-22T16:14:53.020 回答
0

我可以提供一些建设性的建议吗?

如果你做了很多这样的事情,你会遇到两个问题。

首先,采用参数的静态方法通常应该是作为该参数的对象的一部分。我意识到这对像 String 这样的对象没有帮助,但是如果它需要您定义的对象,您几乎可以肯定地通过将您的帮助器作为该对象的方法来改进该对象。

如果它采用所有本机值,您可能可以定义一个对象,它是它的一种方法。看看您是否可以找到这些原生值的任何分组并将它们分组为一个对象。如果你只是尝试一下,你会发现这个小迷你物体还有很多其他用途,而且在你知道它之前它会非常有用。

另一件事,如果你有一个带有一堆半相关的静态方法和静态变量的实用程序类,你几乎总是希望它是一个单例。我通过反复试验发现了这一点,但是当你发现你需要超过 1 个(最终你会)时,将单例变为多例(?)然后尝试将静态类更改为多例(好的,所以我现在正在编造词)。

祝你好运。对我来说,这些东西主要是反复试验——不过就像 5 年前一样,我从来没有找到一个我后悔没有静态类/方法的实例。

于 2009-01-29T21:10:27.087 回答