Могу ли я добавить новые методы в класс String в Java?

Я хотел бы добавить метод AddDefaultNamespace() к классу String в Java, чтобы я мог ввести "myString".AddDefaultNamespace() вместо DEFAULTNAMESPACE + "myString", чтобы получить что-то вроде "MyDefaultNameSpace.myString". Я также не хочу добавлять еще один производный класс (например, PrefixedString).

Возможно, этот подход вам не подходит, но я лично ненавижу использование +. Но, в любом случае, можно ли добавлять новые методы в класс String в Java?

Спасибо и привет.

Было бы здорово иметь возможность обезьяны патчить Java.

jjnguy 17.09.2008 18:28

Java полностью нуждается в методах расширения C# :)

Kurru 05.01.2011 05:11
Многообразие provides comprehensive C#-style метод расширения support for Ява, allowing you to add methods to classes you do not control e.g., java.lang.String. Learn more: http://manifold.systems/docs.htmlhttps://github.com/manifold-systems/manifold
Scott 13.04.2018 00:05
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
27
3
33 745
15
Перейти к ответу Данный вопрос помечен как решенный

Ответы 15

Ответ принят как подходящий

String - это последний класс, что означает, что его нельзя расширить для работы над вашей собственной реализацией.

Это невозможно, поскольку String - это последний класс в Java.

Вы можете использовать вспомогательный метод все время, когда хотите что-то префикс. Если вам это не нравится, вы можете изучить Groovy или Scala, JRuby или JPython, оба являются языками для JVM, совместимыми с Java и допускающими такие расширения.

Невозможно, и это хорошо. Строка - это Строка. Его поведение определено, отклоняться от него было бы злом. Кроме того, он помечен как final, что означает, что вы не можете создать его подкласс, даже если бы захотели.

Лучше использовать StringBuilder, у которого есть метод append () и который выполняет нужную вам работу. Класс String является окончательным и не может быть расширен.

В объявлении класса все сказано, потому что вы не можете унаследовать его, потому что он окончательный. Вы, конечно, можете реализовать свой собственный строковый класс, но это, вероятно, просто хлопот.

public final class String

C# (.net 3.5) имеет возможность использовать методы расширения, но, к сожалению, java этого не делает. Существует некоторое расширение java, называемое nice http://nice.sourceforge.net/, хотя оно, похоже, добавляет ту же функциональность к java.

Вот как вы могли бы написать свой пример на хорошем языке (расширение Ява):

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.

Класс Java String является последним, что делает его неизменным. Это сделано из соображений эффективности, и что было бы чрезвычайно сложно логически расширить без ошибок; поэтому разработчики решили сделать его окончательным классом, что означает, что он не может быть расширен с помощью наследования.

Функциональность, которую вы хотите, чтобы ваш класс поддерживал, не является должным образом частью обычных обязанностей String в соответствии с принцип единой ответственности, пространство имен - это другая абстракция, она более специализирована. Поэтому вам следует определить новый класс, который включает String a member и поддерживает методы, необходимые для обеспечения требуемого управления пространством имен.

Не бойтесь добавлять абстракции (классы) - это суть хорошего объектно-ориентированного дизайна.

Попробуйте использовать карта сотрудничества с классовой ответственностью (CRC), чтобы прояснить нужную вам абстракцию.

Вы можете создать свою версию класса String и добавить метод :-)

Фактически, вы можете изменить класс String. Если вы отредактируете файл String.java, расположенный в src.zip, а затем перестроите rt.jar, класс String будет иметь дополнительные методы, добавленные вами. Обратной стороной является то, что этот код будет работать только на вашем компьютере или в том случае, если вы предоставите свой String.class и поместите его в путь к классам до пути по умолчанию.

Как уже говорили все, вы не можете создать подкласс 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;
    }

}

Как уже отметили все, вам не разрешено расширять 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.

flying sheep 15.02.2011 03:42

Хороший эксперимент, но поскольку безопасно добавлять только новые конструкторы или методы, похоже, нет никаких веских причин использовать этот подход по сравнению с тем, что предложил @ aldo-barreras

Ozzy 10.07.2013 01:09

Все сказано другими участниками ранее. Вы не можете расширить String напрямую, потому что это окончательный вариант.

Если вы будете использовать 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?
  }

Ну, вообще-то все лишены воображения. Мне нужно было написать свою собственную версию метода startWith, потому что мне нужен был тот, который не учитывал регистр.

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

Тогда это довольно просто, вы делаете свою String как таковую:

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

а когда вам нужно вызвать метод в библиотеке String, просто сделайте это так:

StringOne.str.equals("");

или что-то подобное, и вот оно ... расширение класса String.

Думаю нестандартно =).

Ozzy 10.07.2013 01:06

Люди, выполняющие поиск с ключевыми словами «добавить метод к встроенному классу», могут оказаться здесь. Если вы хотите добавить метод к неокончательному классу, например 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");

Нет, вы не можете изменить строковый класс в java. Потому что это последний класс. и каждый метод, присутствующий в классе final, по умолчанию будет окончательным.

Абсолютно самая важная причина того, что String является неизменяемым или окончательным, заключается в том, что он используется механизмом загрузки классов и, таким образом, имеет глубокие и фундаментальные аспекты безопасности.

Если бы String была изменяемой или не окончательной, запрос на загрузку «java.io.Writer» мог быть изменен на загрузку «mil.vogoon.DiskErasingWriter»

ДА!

В зависимости от ваших требований (добавьте другое пространство имен в String и не используйте производный класс) вы можете использовать проект Lombok для этого и использовать функциональность в String следующим образом:

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

Используя идею Gradle и IntelliJ, выполните следующие действия:

  1. Загрузите плагин lombok из репозитория плагинов intelliJ.
  2. добавить ломбок к зависимостям в градиенте, например: 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() для любой строки в любом классе, если вы аннотировали ее и полученный результат для приведенного выше примера.

This is a String!

было бы:

This is a String! 17

IntelliJ выделяет мой метод расширения, как будто это ошибка, а затем он отлично компилируется.

Noah Wilder 27.10.2018 02:06

Это не расширение класса, только метод начальной загрузки может заменить класс String. Однако он очень хорошо имитирует добавление методов, перекомпилируя Main для вызова статических методов в Extension. Это идеальный ответ на этот вопрос. Но будьте осторожны, если вы вызовете String.class.getDeclaredMethod, он не будет найден.

Dyorgio 13.12.2020 20:47

Другие вопросы по теме