悪いJavaまたはそれをしない方法

仕事中、私は、おそらく皆さんそれぞれと同様に、軽微なJavaの欠陥に気づかなければならない場合があります。 小さくてまれですが、固有のものです。 私の最初の投稿に対するコメントの 1つは、この記事を書いた偉業です。 このトピックは私にとって非常に興味深く思えたので、お気に入りのプログラミング言語で好きではないものすべてを思い出すことにしました。 それでは、始めましょう:



ハッシュセット


このような決定が下された理由はわかりませんが、HashSetがHashMapに実装されました。そうです-作成にかかる時間を節約しましたが、これは主要なコレクションの1つです。 それでも、HashSetをより最適に作成することは可能でした。 HashMapは、HashSetタスクのコンテキストで冗長アーキテクチャをもたらします。 たとえば、HashSet内には次のコードがあります。

// Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object();
      
      





これは、HashSet内の任意の値がこのオブジェクトへのリンクに関連付けられることを意味します。 これは次と同じです:

 Map.put(key, PRESENT);
      
      





みなさんが使用する8バイトのように思えます。 ただし、HashSetに挿入するたびに、Map.Entryが作成されることを忘れないでください。4つのリンク(要素ごとにさらに16バイト)があります。 無駄ですよね? なぜそう 大きな謎...少なくとも受け継がれていないありがとう。



デフォルトのロガー


プロジェクトでlog4jを使用しないのは誰ですか? それなしでもできるライブラリに名前を付けることはできますか? これらは難しい質問だと思います。 javaは特定のタスクごとに調整できないことを理解していますが、標準のLoggerが追加されたので、なぜlog4jが10年にわたって存在していたのに、Javaはそれを最大限に活用できなかったのですか? すべてのアプリケーション、特に複雑なアプリケーションがどれだけ減少するかを想像してください。最終アセンブリでは、ロガーのいくつかの異なるバージョンが表示される可能性があります。



クローン可能


Clonableインターフェースが空であり、単一のメソッドと変数がなく、JVMのマーカーにすぎないことは誰もが知っています。 問題はなぜですか? 結局、継承する場合、何らかの動作を決定しようとしています。OOPパラダイムに従って、この動作を決定するclone()メソッドを含める必要があります。 もちろん、クローンを再定義せずにオブジェクトのコピーを取得すると便利な場合があります。 ただし、実際には、コレクション内の同じオブジェクトへのリンクだけでなく、完全なコピーを取得するために、少なくとも1つのモデルオブジェクトのコレクションを持つすべてのクラスでこのメソッドをオーバーライドする必要があります。 開発者がこのインターフェイスで単一のメソッドを定義しなかった理由はわかりません。 おそらく忘れてしまったかもしれませんが、彼らにこの理由があったことを願っています。 その結果、Objectのclone()メソッド。



シリアライズ可能


状況はClonableに似ていますが、ここでは少なくとも1つは議論することができます-ほとんどの場合

動作を手動で定義するのではなく、標準の実装に依存しているため、一部のシリアル化メソッドを常に再定義し、新しいフィールドの追加を常に監視してメソッドに追加するのは非常に不便です。 まあ、特にこれらの目的のためにExternalizableがあります。 ただし、writeObject()およびreadObject()を事前定義することにより、標準のシリアル化動作を変更できることがわかっています。 どういうわけかOOPshnyではありません。



物性


何らかの方法で、私たちはすべてPropertiesクラスを使用します。 私たちはオープンソースコードと私たちが見ているものを?

 class Properties extends Hashtable<Object,Object>
      
      





なんで? 特に継承が論理的な意味を持たず、アプリケーションモデルを単純化しない場合、構成が継承よりも優れていることを誰もが知っています。 load(InputStream inStream)およびload0(LineReader lr)メソッドはどうですか? しかし、メソッドのオーバーロードはどうでしょうか? 同じことがstore0()にも当てはまります。クラス内に隠されていてもです。 ちなみに、このようなメソッドは、誰かのスタイルに似たコアjavaのどこにでもあります。



プリミティブ型のシェルクラス


シェルクラスは冗長です。プリミティブを操作し、それらにnull値を割り当てることができればはるかに便利です。 単純なintが4バイトを使用する場合、Integerオブジェクトは既に整数16です。定数キャスト、等号による比較。 なんでこんなこと? 次:

 Boolean.getBoolean(); Long.getLong(); Integer.getInteger()
      
      





神秘的な方法。 私はまだ彼らが果たすべき役割となぜその方法が理解できないのか

System.getProperty()がシェルのクラスにあり、クラスのプロパティにないことを確認します。



スイッチ


もちろん、すべてのJava開発者は、switchステートメントが整数型でのみ機能し、7番目のJavaと文字列でのみ動作することを知っています。 しかし、これらの制限はなぜですか? バージョン7のスイッチの実装から判断すると、hashode()およびequals()に基づく文字列で動作します。 次に質問は-なぜ文字列だけですか? オブジェクトに演算子を実装するのは簡単でしたが、何らかの理由でこれは行われませんでした。 別のなぞなぞ。



コレクション


ListとSetには素敵なretainAll()メソッドがあります-交差するセットですが、何らかの理由で差分セットのメソッドはありません。 もちろん、自分で簡単に実装できます。

 List first = someList(); List second = someList(); diff = first.clone(); diff.removeAll(second);
      
      





ただし、主要なデータ型の1つを使用したこのような基本的な操作は、すぐに使用できるようにしたいと思います。



さらに、コレクションを操作する際の最も一般的なタスクの1つは、コレクション内の目的のアイテムを見つけることです。 ListにindexOf(オブジェクトo)があり、HashSetにget(オブジェクトo)がないのはなぜですか。これは、ハッシュコードと等価によってコレクションからオブジェクトへの参照を返します。 はい、ハッシュコードが再定義されていない場合、これは意味がありませんが、定義されていない場合は、既に使用の機会があります。 たぶん、私は過失を見つけすぎるかもしれませんが、そのようなタスクが頻繁に発生します。



同じカテゴリから-一部のプロパティに従ってオブジェクトをフィルタリングする方法はありません。 常に手動で行う必要があります。 悪い、非常に悪い。



ジェネリック


 Map<MyKeyObject, PersistedPlan> plansById = new HashSet<MyKeyObject, PersistedPlan>();
      
      





すでに述べたように、そのような構造はコードの至るところに見られます-クラスの説明、コンストラクター、メソッド、どこにでも...コードを非常に過負荷にし、認識を複雑にします。 幸いなことに、第7 Javaではこれは修正されました。 それだけでは明らかではありません-なぜ彼らはそんなに長く待ったのですか?



マルチスレッド


Erlangでマルチスレッドについて初めて読んだとき、Javaマルチスレッドメカニズムがどれほど複雑であるかを知りました。 これらのロック、モニター、同期、デッドロック、シーアド、実行可能ファイル、メモリーバリアのすべて。 なんで? javaが20歳以上であるため、なぜ私たちの生活がそれほど複雑なのか、私たちの時代の傾向を考慮することは本当に難しいのでしょうか? 結局のところ、Javaの主な目標は、私たちにとって生活を楽にすることでした-シンプルな開発者です。

PS私はここで同僚と話をしましたが、この方向での進展は小さくないという情報があるので、私たちは願っています。



短絡


関数をパラメーターとして裏切る必要があることがよくありますよね? どれくらい待つことができますか? Java開発者として、最も予想される改善の1つが行われなかったことに非常に満足していません。 彼らが少なくとも8日に登場することを期待しましょう、追加するものは何もありません。



ランタイムとシステム


これらのクラスの論理的な分離は完全には明らかではありませんが、それで十分なようです。 Systemに関連するその他の奇妙な点がいくつかあります-たとえば、クラスのoutおよびinフィールド-final。 ただし、同時にsetOutメソッドとsetInメソッドがあります。 悪い例ですね。



ひも


非常に非効率的です。各文字が2バイトを占有するだけでなく、各行はStringクラスのオブジェクトであり、これはヘッダーごとに8バイト+内部に3つの整数値+ 1つの配列参照==余分な24バイトですひも! まだ少し高い。 少なくとも2つのラインのバリアントを作成することは可能です。安いラインと高いラインです。 少なくとも選択肢があります。



数字


 Long l = 2; //compilation error Map<int, Object> a = new HashMap<int, Object>(); //compilation error Map<Object, int> a = new HashMap<Object, int>(); //compilation error Set<int> b = new HashSet<int>(); //compilation error
      
      





なぜこのような単純なことを複雑にするのですか? まだ分​​かりません もちろんささいなことですが、良くありません。 さらに-標準プリミティブはJavaで浮動小数点数を扱うのに適していないことを誰もが知っています。なぜなら、それらは精度を失うからです。 さて、別のタイプBigDecimalを使用します。 そして、これは単純な式が次のようになることです:

 //(metric.getValue() * 100 / prevMetric.getValue()) + metric.getOffset() BigDecimal result =BigDecimal.valueOf(metric.getValue() * 100).divide(BigDecimal.valueOf(prevMetric.getValue()).add(metric.getOffset()));
      
      





さて、計算が単純で複雑であれば、すべてが非常に悲しいものになります。 doubleとfloatに加えて数値でうまく機能するデータ型を導入してみませんか?



結論


これが何らかの秋の悪化であるとは思わないでください。 まさか。 私は本当にjavaが好きで、私たちの時代のトレンドに合わせてJavaを開発してほしいです。 そして、面白くて面白い仕事を煩わせ、気を散らすだけのこれらのささいなことなしに、喜んでそれをプログラムできるようにしたい。



All Articles