web-dev-qa-db-ja.com

JavaのStringクラスに新しいメソッドを追加できますか?

JavaのStringクラスにメソッドAddDefaultNamespace()を追加して、_DEFAULTNAMESPACE + "myString"_の代わりに"myString".AddDefaultNamespace()と入力できるようにします。 _"MyDefaultNameSpace.myString"_のようなものを取得します。別の派生クラス(たとえば、PrefixedString)も追加したくありません。

このアプローチはあなたにとって良くないかもしれませんが、私は個人的に_+_を使うのが嫌いです。しかし、とにかく、JavaのStringクラスに新しいメソッドを追加することは可能ですか?

よろしくお願いします。

24
user15546

Stringは最終クラスです。つまり、独自の実装で動作するように拡張することはできません。

65
GustyWind

まあ、実際には誰もが想像を絶するものです。大文字と小文字を区別しない独自のバージョンのstartsWithメソッドを作成する必要がありました。

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

次に、それは非常に簡単です。文字列を次のように作成します。

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

文字列ライブラリのメソッドを呼び出す必要がある場合は、次のように簡単に実行できます。

StringOne.str.equals("");

または同様の何か、そしてあなたはそれを持っています... Stringクラスの拡張。

24
Aldo Barreras

他の誰もが指摘しているように、Stringを拡張することは許可されていません(finalのため)。ただし、本当にワイルドに感じている場合は、String自体を変更してjarに配置し、bootclasspathの前に-Xbootclasspath/p:myString.jarを追加して、組み込みのStringクラスを実際に置き換えることができます。

入らない理由で、私は実際にこれを以前に行ったことがあります。クラスを置き換えることはできますが、Javaのすべての面でStringが本質的に重要であることは、JVMの起動全体で使用され、一部の変更は単純に行われることを意味します。 JVMを壊します。新しいメソッドやコンストラクターを追加することは問題ないようです。新しいフィールドを追加することは非常に厄介です。特に、オブジェクトや配列を追加すると、プリミティブフィールドを追加することはできますが、問題が発生するようです。

18
Alex Miller

StringはJavaの最終クラスであるため、これは不可能です。

何かにプレフィックスを付けたいときはいつでもヘルパーメソッドを使用できます。それが気に入らない場合は、GroovyまたはScalaを調べることができます。JRubyまたはJPythonはどちらも、Javaと互換性があり、そのような拡張機能を許可するJVM用の言語です。

6
jrudolph

他のみんなが言っているように、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;
    }

}
2
MB.

不可能です、そしてそれは良いことです。文字列は文字列です。その振る舞いは定義されており、それから逸脱することは悪です。また、finalとマークされているため、必要な場合でもサブクラス化できませんでした。

2
Sietse

「組み込みクラスにメソッドを追加する」というキーワードで検索する人は、ここにたどり着くかもしれません。 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");
1
HeisenBerg

いいえJavaで文字列クラスを変更することはできません。ファイナルクラスだから。そして、デフォルトでfinalクラスに存在するすべてのメソッドがfinalになります。

Stringが不変またはfinalであるという絶対的に最も重要な理由は、Stringがクラスのロードメカニズムによって使用されるため、セキュリティの面で深く基本的な側面があるためです。

Stringが変更可能であるか、最終的でない場合、「Java.io.Writer」をロードする要求は「mil.vogoon.DiskErasingWriter」をロードするように変更されている可能性があります。

1
Vpn_talent

はい!

要件に基づいて(別の名前空間を文字列に追加し、派生クラスを使用しない)、プロジェクトLombokを使用してそれを実行し、次のような文字列の機能を使用できます。

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

GradleとIntelliJのアイデアを使用するには、以下の手順に従います。

  1. IntelliJプラグインリポジトリからlombokプラグインをダウンロードします。
  2. 次のようにgradleの依存関係にlombokを追加します:_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

1
PaulB

メソッドappend()を持ち、必要なジョブを実行するStringBuilderを使用することをお勧めします。 Stringクラスはfinalであり、拡張できません。

1
m_pGladiator

すべては前に他の貢献者によって言われています。 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?
  }
0
user252175

Java Stringクラスは最終クラスであり、不変になります。これは効率上の理由であり、エラーなしで論理的に拡張することは非常に困難です。したがって、実装者はこれを最終クラスにすることを選択しました。つまり、継承によって拡張することはできません。

クラスでサポートしたい機能は、 単一責任の原則 のように、文字列の通常の責任の一部ではありません。名前空間は別の抽象化であり、より専門的です。したがって、Stringメンバーを含み、必要な名前空間管理を提供するために必要なメソッドをサポートする新しいクラスを定義する必要があります。

抽象化(クラス)を追加することを恐れないでください。これらは優れたOO設計の本質です。

クラス責任コラボレーション(CRC)カード を使用して、必要な抽象化を明確にしてください。

0
Martin Spamer