【问题标题】:Can I add new methods to the String class in Java?我可以向 Java 中的 String 类添加新方法吗?
【发布时间】:2010-09-10 01:18:36
【问题描述】:

我想在 Java 中的 String 类中添加一个方法 AddDefaultNamespace(),这样我就可以键入 "myString".AddDefaultNamespace() 而不是 DEFAULTNAMESPACE + "myString",以获得类似 "MyDefaultNameSpace.myString" 的内容。我也不想添加另一个派生类(例如PrefixedString)。

也许这种方法对您不利,但我个人讨厌使用+。但是,无论如何,是否可以在 Java 中为 String 类添加新方法?

感谢和问候。

【问题讨论】:

标签: java string


【解决方案1】:

String 是一个 final 类,这意味着它不能扩展为在您自己的实现上工作。

【讨论】:

    【解决方案2】:

    嗯,实际上每个人都缺乏想象力。我需要编写自己版本的 startsWith 方法,因为我需要一个不区分大小写的方法。

    class MyString{
        public String str;
        public MyString(String str){
            this.str = str;
        }
        // Your methods.
    }
    

    那么就很简单了,你把你的字符串变成这样:

    MyString StringOne = new MyString("Stringy stuff");
    

    当你需要调用 String 库中的方法时,只需这样做:

    StringOne.str.equals("");
    

    或类似的东西,你有它...... String 类的扩展。

    【讨论】:

    • 开箱即用 =)。
    【解决方案3】:

    正如其他人所指出的,您不能扩展 String(由于 final)。但是,如果你觉得很疯狂,你可以修改 String 本身,将它放在一个 jar 中,并在 bootclasspath 前面加上 -Xbootclasspath/p:myString.jar 来实际替换内置的 String 类。

    由于我不会详细介绍的原因,我实际上之前已经这样做过。您可能有兴趣知道,即使您可以替换该类,String 在 Java 的各个方面的内在重要性意味着它在整个 JVM 启动过程中都会使用,并且某些更改只会破坏 JVM。添加新方法或构造函数似乎没有问题。添加新字段非常冒险 - 特别是添加对象或数组似乎会破坏事物,尽管添加原始字段似乎有效。

    【讨论】:

    • 我会试试这个。字符串需要是可迭代的。当我们在做的时候:java怎么会这么糟糕?没有理智的人会在界面中指定remove,只是添加throws UnsupportedOperationException - if the remove operation is not supported by this Iterator
    • 不错的实验,但由于添加新的构造函数或方法是安全的,因此似乎没有任何正当理由采用这种方法而不是 @aldo-barreras 建议的内容
    【解决方案4】:

    这是不可能的,因为 String 是 Java 中的 final 类。

    您可以在任何时候都使用辅助方法来为某些内容添加前缀。如果你不喜欢,你可以研究一下 Groovy 或 Scala,JRuby 或 JPython 都是 JVM 与 Java 兼容的语言,并且允许这样的扩展。

    【讨论】:

      【解决方案5】:

      类声明说明了一切,因为你不能继承它,因为它是最终的。 您当然可以实现自己的字符串类,但这可能只是个麻烦。

      public final class String
      

      C# (.net 3.5) 具有使用扩展方法的功能,但遗憾的是 java 没有。有一些名为 nice http://nice.sourceforge.net/ 的 java 扩展,虽然它似乎为 java 添加了相同的功能。

      以下是您将如何使用 Nice 语言编写示例( 爪哇):

      private String someMethod(String s)
      {
         return s.substring(0,1);
      
      }
      
      void main(String[] args)
      {
         String s1 = "hello";
         String s2 = s1.someMethod();
         System.out.println(s2);
      
      }
      

      你可以在http://nice.sf.net找到更多关于尼斯的信息

      【讨论】:

        【解决方案6】:

        是的!

        根据您的要求(向字符串添加不同的命名空间而不使用派生类),您可以使用 Lombok 项目来做到这一点,并在字符串上使用如下功能:

        String i = "This is my String";
        i.numberOfCapitalCharacters(); // = 2
        

        使用 Gradle 和 IntelliJ IDEA 的步骤如下:

        1. 从 intelliJ 插件库下载 lombok 插件。
        2. 将 lombok 添加到 gradle 中的依赖项,如下所示:compileOnly 'org.projectlombok:lombok:1.16.20'
        3. 转到"Settings > Build > Compiler > Annotation Processors" 并启用注释处理
        4. 使用您的扩展函数创建一个类并添加一个静态方法,如下所示:

          public class Extension {
              public static String appendSize(String i){
                  return i + " " + i.length();
              }
          }
          
        5. 像这样注释你想使用你的方法的类:

          import lombok.experimental.ExtensionMethod;
          
          @ExtensionMethod({Extension.class})
          public class Main {
              public static void main(String[] args) {
                  String i = "This is a String!";
                  System.out.println(i.appendSize());
              }
          }
          

        现在您可以在任何类中的任何字符串上使用方法.appendSize(),只要您已对其进行注释并为上述示例生成结果

        这是一个字符串!

        应该是:

        这是一个字符串! 17

        【讨论】:

        • IntelliJ 突出显示我的扩展方法,好像它是一个错误,然后编译正常。
        • 这不是类扩展,只有引导方法可以替换String类。但是它很好地模拟了方法添加,重新编译 Main 以在 Extension 上调用静态方法。是这个问题的完美答案。但是要注意,如果调用String.class.getDeclaredMethod,是找不到的。
        【解决方案7】:

        不可能,这是件好事。一个字符串就是一个字符串。它的行为是有定义的,偏离它就是邪恶的。此外,它被标记为 final,这意味着即使您愿意,也无法对其进行子类化。

        【讨论】:

          【解决方案8】:

          正如其他人所说,不,你不能继承 String 因为它是最终的。但是可能会有类似以下的帮助吗?

          public final class NamespaceUtil {
          
              // private constructor cos this class only has a static method.
              private NamespaceUtil() {}
          
              public static String getDefaultNamespacedString(
                      final String afterDotString) {
                  return DEFAULT_NAMESPACE + "." + afterDotString;
              }
          
          }
          

          或者也许:

          public final class NamespacedStringFactory {
          
              private final String namespace;
          
              public NamespacedStringFactory(final String namespace) {
                  this.namespace = namespace;
              }
          
              public String getNamespacedString(final String afterDotString) {
                  return namespace + "." + afterDotString;
              }
          
          }
          

          【讨论】:

            【解决方案9】:

            使用关键字“将方法添加到内置类”搜索的人可能会在这里结束。如果您希望将方法添加到诸如 HashMap 之类的非最终类,您可以执行类似的操作。

            public class ObjectMap extends HashMap<String, Object> {
            
                public Map<String, Object> map;
            
                public ObjectMap(Map<String, Object> map){
                    this.map = map;
                }
            
                public int getInt(String K) {
                    return Integer.valueOf(map.get(K).toString());
                }
            
                public String getString(String K) {
                    return String.valueOf(map.get(K));
                }
            
                public boolean getBoolean(String K) {
                    return Boolean.valueOf(map.get(K).toString());
                }
            
                @SuppressWarnings("unchecked")
                public List<String> getListOfStrings(String K) {
                    return (List<String>) map.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public List<Integer> getListOfIntegers(String K) {
                    return (List<Integer>) map.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public List<Map<String, String>> getListOfMapString(String K) {
                    return (List<Map<String, String>>) map.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public List<Map<String, Object>> getListOfMapObject(String K) {
                    return (List<Map<String, Object>>) map.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public Map<String, Object> getMapOfObjects(String K) {
                    return (Map<String, Object>) map.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public Map<String, String> getMapOfStrings(String K) {
                    return (Map<String, String>) map.get(K);
                }
            }
            

            现在定义这个类的一个新实例:

            ObjectMap objectMap = new ObjectMap(new HashMap<String, Object>();
            

            现在您可以访问内置 Map 类的所有方法,以及新实现的方法。

            objectMap.getInt("KEY");
            

            编辑:

            在上面的代码中,要访问地图类的内置方法,你必须使用

            objectMap.map.get("KEY");
            

            这里有一个更好的解决方案:

            public class ObjectMap extends HashMap<String, Object> {
            
                public ObjectMap() {
            
                }
            
                public ObjectMap(Map<String, Object> map){
                    this.putAll(map);
                }
            
                public int getInt(String K) {
                    return Integer.valueOf(this.get(K).toString());
                }
            
                public String getString(String K) {
                    return String.valueOf(this.get(K));
                }
            
                public boolean getBoolean(String K) {
                    return Boolean.valueOf(this.get(K).toString());
                }
            
                @SuppressWarnings("unchecked")
                public List<String> getListOfStrings(String K) {
                    return (List<String>) this.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public List<Integer> getListOfIntegers(String K) {
                    return (List<Integer>) this.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public List<Map<String, String>> getListOfMapString(String K) {
                    return (List<Map<String, String>>) this.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public List<Map<String, Object>> getListOfMapObject(String K) {
                    return (List<Map<String, Object>>) this.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public Map<String, Object> getMapOfObjects(String K) {
                    return (Map<String, Object>) this.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public Map<String, String> getMapOfStrings(String K) {
                    return (Map<String, String>) this.get(K);
                }
            
                @SuppressWarnings("unchecked")
                public boolean getBooleanForInt(String K) {
                    return Integer.valueOf(this.get(K).toString()) == 1 ? true : false;
                }
            }
            

            现在你不用打电话了

            objectMap.map.get("KEY");
            

            只需调用

            objectMap.get("KEY");
            

            【讨论】:

              【解决方案10】:

              最好使用 StringBuilder,它有方法 append() 并且可以完成你想要的工作。 String 类是最终类,不能扩展。

              【讨论】:

                【解决方案11】:

                不,您不能在 java 中修改字符串类。因为这是最后一节课。并且默认情况下 final 类中存在的每个方法都是 final 的。

                String 是不可变或最终的绝对最重要的原因是它被类加载机制使用,因此具有深刻和基本的安全方面。

                如果 String 是可变的或不是最终的,加载“java.io.Writer”的请求可能已更改为加载“mil.vogoon.DiskErasingWriter”

                【讨论】:

                  【解决方案12】:

                  以上都是其他贡献者所说的。不能直接扩展 String,因为它是 final 的。

                  如果你会使用 Scala,你可以像这样使用隐式转换:

                  object Snippet {
                    class MyString(s:String) {
                      def addDefaultNamespace = println("AddDefaultNamespace called")
                    }
                    implicit def wrapIt(s:String) = new MyString(s)
                  
                    /** test driver */
                    def main(args:Array[String]):Unit = {
                      "any java.io.String".addDefaultNamespace // !!! THAT is IT! OR?
                    }
                  

                  【讨论】:

                    【解决方案13】:

                    Java String 类是一个final,使其不可变。这是出于效率原因,并且在逻辑上没有错误地扩展将是极其困难的;因此,实现者选择将其设为最终类,这意味着它不能通过继承进行扩展。

                    根据single responsibility principle,您希望您的类支持的功能不是字符串的常规职责的一部分,它是一个不同的抽象,它更专业。因此,您应该定义一个新类,其中包含 String 一个成员并支持您提供所需的命名空间管理所需的方法。

                    不要害怕添加抽象(类),这些是优秀 OO 设计的精髓。

                    尝试使用class responsibility collaboration (CRC) card 来阐明您需要的抽象。

                    【讨论】:

                      【解决方案14】:

                      您可以创建自己的 String 类版本并添加方法 :-)

                      【讨论】:

                        【解决方案15】:

                        其实你可以修改String类。如果您编辑位于 src.zip 中的 String.java 文件,然后重新构建 rt.jar,则 String 类将拥有更多由您添加的方法。缺点是该代码只能在您的计算机上运行,​​或者如果您提供您的 String.class 并将其放置在默认路径之前的类路径中。

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2016-06-06
                          • 1970-01-01
                          • 2011-10-04
                          • 2011-11-21
                          • 1970-01-01
                          • 2015-11-29
                          相关资源
                          最近更新 更多