ラベル Java の投稿を表示しています。 すべての投稿を表示
ラベル Java の投稿を表示しています。 すべての投稿を表示

2017年10月21日土曜日

JShellを使ってJavaだけでSelenium (ChromeDriver) を動かす。

はじめに

Mirror House Lab: Nashornを使ってJavaScriptを書いてSelenium (ChromeDriver) を動かす。で、
「もうすぐjshellを含むJava SE 9がリリースされてJavaをスクリプトとして実行できるようになるだろうに、何をやっているんだ」という気もしているけれど、Java SE 8環境ですぐに動かしたかったので。
と言っていた。

Java SE 9がリリースされたのでJShellで動かす方法について書く。

環境と準備

環境は以下の通り。
  • OS: Windows 10 (64bit)
  • JDK: 9
  • Selenium Standalone Server: 3.6.0
  • Chrome: 61
  • ChromeDriver: 2.33

SeleniumとChromeDriverの準備作業は変わらない。ただしNashhornとJShellでクラスパスの通し方が異なる(後述)。JDKとChromeはインストール済みとする。

Selenium: DownloadsからSelenium Standalone Serverをダウンロードする。保存先フォルダにパスを通すのは必須ではない。JShellはオプションでもコマンドでもクラスパスを指定できる。この記事ではコマンドで指定する方法を採用する。

ChromeDriver: Downloads - ChromeDriver - WebDriver for Chromeから、chromedriver_win32.zipをダウンロードして展開する。Windows 64bit用は提供されていないので32bit用を使う。こちらも保存先にパスを通すのは必須ではない。Javaの場合、プロパティに設定できる。このエントリィではその方法を採用する。

サンプルコード

クラスパスを通すのはJShellのコマンド。あとはJavaなので取り立てて言うことはない。
// jshell-selenium.jsh
/env -class-path C:\path\to\selenium-server-standalone-3.6.0.jar;

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

String pathToChromeDriver = "C:\\path\\to\\chromedriver.exe";
System.setProperty("webdriver.chrome.driver", pathToChromeDriver);

ChromeDriver driver = new ChromeDriver();
driver.get("http://www.google.com/xhtml");
Thread.sleep(5000);
WebElement searchBox = driver.findElement(By.name("q"));
searchBox.sendKeys("ChromeDriver");
searchBox.submit();
Thread.sleep(5000);
driver.quit();

実行方法

最初はJShellを起動して、下記を実行する。
> /open C:\path\to\jshell-selenium.jsh

2回目以降、そのまま繰り返すときは/reloadコマンド。

スクリプトファイルを修正して再実行するなら、/resetコマンドを実行してから、/openコマンド。JShellは/listで表示されるコードを実行するのだけれど、/openコマンドは追記して実行するので、もう1回/openコマンドでスクリプトファイルを読み込むと2回実行される。

クラスパスの通し方

クラスパスは、JShell起動時にコマンドラインオプションで渡すこともできる。External Codeの"Setting The Class Path"を参照。

Java 9なのでクラスパスではなくてモジュールも使える(External Codeの"Setting Module Options")。でも、そのモジュールをまだよく分かっていないので、まずはそちらから調べないと。

感想

Javaで書けるので言語差分を考えなくてよいのと、ちょっとデバッグしているときにJShell上で補完が効くのが助かる。

References

2017年7月15日土曜日

Nashornを使ってJavaScriptを書いてSelenium (ChromeDriver) を動かす。

はじめに

開発環境ではない環境で、ちょっとしたブラウザ操作を自動化するのに使った方法について書く。

具体的には、Nashorn (Java SE 8で追加されたJavaScriptエンジン。Rhinoの後継) を使い、JavaScriptを書いてSelenium Standalone Serverを呼び出し、ChromeDriverでChromeを自動操作する方法。

「Selenium Standalone ServerはJarなのだから、素直にJavaから呼んだら?」とか、「JavaScriptで書くならSelenium Client & WebDriver Language BindingsのJavaScript (node)を使うのが自然では?」とも思うのも、ごもっとも。自分もそう思う。

それでもこの方法を調べたのは、環境準備が楽だから。JREさえインストールされていれば、あとはSeleniumをダウンロードするだけ。Eclipse, JDK, node.jsどれも必要ない。

「もうすぐjshellを含むJava SE 9がリリースされてJavaをスクリプトとして実行できるようになるだろうに、何をやっているんだ」という気もしているけれど、Java SE 8環境ですぐに動かしたかったので(※2017/10/21追記: JShellでも動かしてみた)。

環境と準備

環境は以下の通り。
  • OS: Windows 10 (64bit)
  • JRE: 8
  • Selenium Standalone Server: 3.4.0
  • Chrome: 59
  • ChromeDriver: 2.30

SeleniumとChromeDriverの準備作業は以下の通り。JREとChromeはインストール済みとする。

Selenium: DownloadsからSelenium Standalone Serverをダウンロードする。保存先フォルダにパスを通すのは必須ではない。Nashornを起動するときにオプションでクラスパスを指定できる。この記事ではその方法を採用する。

ChromeDriver: Downloads - ChromeDriver - WebDriver for Chromeから、chromedriver_win32.zipをダウンロードして展開する。Windows 64bit用は提供されていないので32bit用を使う。こちらも保存先にパスを通すのは必須ではない。Javaの場合、プロパティに設定できる。このエントリィではその方法を採用する。

サンプルコード

ポイントはJavaクラスへのアクセス。標準JavaパッケージのクラスにアクセスするにはFQCNを使う。それ以外のクラス(ここではorg.openqa.seleniumパッケージのクラス)にアクセスするには、Java.type('FQCN')を使う。
// nashorn-selenium.js

// グローバル変数/関数を作らないため即時関数呼び出し。
// 『メンテナブルJavaScript』の「6.4 0-グローバルのアプローチ」参照。
// この使い方の場合、ここまでする必要もないかもしれないけれど一応。
(function() {
  var ChromeDriver, By;
  var pathToChromeDriver, driver, searchBox;

  // Javaのimport相当。
  ChromeDriver = Java.type("org.openqa.selenium.chrome.ChromeDriver");
  By = Java.type("org.openqa.selenium.By");

  // chromedriver.exeへのパスをプロパティに設定。
  // chromedriver.exeが環境変数PATHに指定しているフォルダにあるなら省略できる。
  pathToChromeDriver = 'C:\\\\path\\to\\chromedriver.exe';
  java.lang.System.setProperty('webdriver.chrome.driver', pathToChromeDriver);

  // 実行内容は、
  //   Getting started - ChromeDriver - WebDriver for Chrome
  //   https://sites.google.com/a/chromium.org/chromedriver/getting-started
  // と同じ。
  // Chromeを起動して、Googleで"ChromeDriver"を検索して、Chromeを終了する。
  driver = new ChromeDriver();
  driver.get('http://www.google.com/xhtml');
  java.lang.Thread.sleep(5000);
  searchBox = driver.findElement(By.name('q'));
  searchBox.sendKeys('ChromeDriver');
  searchBox.submit();
  java.lang.Thread.sleep(5000);
  driver.quit();
}());

実行方法

コマンドプロンプトからjjs (nashornを起動するツール) を実行する。その際、-cpオプションでSelenium Standalone Serverをクラスパスに追加しておく。
> jjs -cp "C:\path\to\selenium-server-standalone-3.4.0.jar" nashorn-selenium.js

感想

もろもろの補完が効かないから、スクリプトを書くのが疲れる。けれど、IDE入れるなら環境を整えて、Javaから動かせばよいので、どちらの手間を惜しむかか。

References

2015年8月2日日曜日

JUnitのRunner (Enclosed, Theories, Categories) を併用したときの動き

JUnit4の次のRunnerの関係を整理してみた。なお、JUnitのバージョンは4.12。
  • Enclosed
  • Theories
  • Categories

論理的にテストクラスを分類しつつ、並列化するための分類もしたくなることがあるので、JUnitの仕組みでどこまでできるか知っておきたくて。

では、早速。

EnclosedとTheoriesは併用できる。外部クラスEnclosingTheoryをJUnit実行すれば、内部クラスEnclosedTheoryがJUnit実行される。これでTheoriesを使いたいけれど、テストクラスを分けたくない場合は大丈夫。
@RunWith(Enclosed.class)
public class EnclosingTheory {
    @RunWith(Theories.class)
    public static class EnclosedTheory {
        @Theory
        public void testTheory(Fixture f) throws Exception {
            // test method.
        }
    }
}

EnclosedとCategoriesは併用できない。外部クラスEnclosingTheoryにカテゴリOuterを付けて、Categoriesを使ったテストスイートを実行しても、テストが見つからない。
@RunWith(Enclosed.class)
@Category(Outer.class)
public class EnclosingTheory {
    @RunWith(Theories.class)
    public static class EnclosedTheory {
        @Theory
        public void testTheory(Fixture f) throws Exception {
            // test method.
        }
    }
}
@RunWith(Categories.class)
@IncludeCategory(Outer.class)
@SuiteClasses(EnclosingTheory.class)
public class CategorizedTestSuite {
    // NoTestsRemainException is thrown.
}

ただ、内部クラスEnclosedTheoryを直接指定すれば、実行させられる。Enclosedの甲斐がないと見るか、互いに独立した分類が使えると見るか、悩ましい。
@RunWith(Enclosed.class)
public class EnclosingTheory {
    @RunWith(Theories.class)
    @Category(Inner.class)
    public static class EnclosedTheory {
        @Theory
        public void testTheory(Fixture f) throws Exception {
            // test method.
        }
    }
}
@RunWith(Categories.class)
@IncludeCategory(Inner.class)
@SuiteClasses(EnclosedTheory.class)
public class CategorizedTestSuite {
    // run testTheory.
}

上記でテストが実行されるので、CategoriesとTheoriesは併用できることが分かる。

調べて見ると、CategoriesとEnclosedはそれぞれSuiteのサブクラスだった。併用できないのもさもありなん。

以下は、調べながら考えたことをつらつらと。

こうして調べて見ると、Categoryアノテーションで並列化のための分類をするのは筋が悪い気がしてきた。MECEに分割したいのだけれど、アノテーションの付け忘れやテストスイートへの追加忘れがありそう。何並列にするかによるけれど、上位のパッケージ構成でざっくり割っちゃった方が安全かなぁ。

CIとの相性も考える必要がある。AntのJUnitタスクから実行するなら、パッケージ構成やファイル命名規約に加えてFileSetで色々できる(開発端末上では使えないけれど)。一方、MavenはCategoryにも対応している。そろそろAntから卒業した方がいい気がしてきた……。

なお、調べるために書いたコードはso-c/junit4.12-categories-configuration-sampleにアップしてある。ここに書いたスニペットよりゴチャゴチャしているけれど、ちゃんと動くのでこれはこれで。

2013年12月9日月曜日

Javaのアサーション (assert文) の使いどころ

Javaのアサーションについて、少し調べたことをメモしておく。『言語設計者が考えること』の「12章 Java」で、Javaプログラマへの助言として
assert文を至る所に散りばめること
なんて書かれていたけれど、使いどころがよく分らなかったのが、調べたキッカケ。

アサーションを使用したプログラミング (Java言語仕様) によると、使いどころは次の3つ。リンク先には具体例がもあるので、これを見るとおおよその想像がつく。
  • 内部の不変条件
  • 制御フローの不変条件
  • 事前条件、事後条件、およびクラスの不変条件

この内、3つ目の「事前条件、事後条件、およびクラスの不変条件」のさらに「事前条件」には「publicメソッドの引数チェックに使ってはいけない」という但し書きが付く。publicメソッドの引数が満たすべき事前条件はAPI仕様の一部だというのがその理由。『Java セキュアコーディングスタンダード 』の「MET01-J. メソッドの引数の検証にassertを使わない」に詳しい。
アサーションは、public メソッド内の引数チェックに使用しないでください。

引数のチェックは通常、メソッドの仕様 (または規約) の一部になっており、アサーションの有効/無効にかかわらず、この仕様に準拠する必要があります。アサーションを使用して引数をチェックした場合、不正な引数によってランタイム例外 (IllegalArgumentException、IndexOutOfBoundsException、NullPointerException など) が発生する可能性があります。アサーションが失敗しても、適切な例外はスローされません。

「適切な例外をスローする」とあるけれど、例外は使わないで通常の制御フローを前提にアサーションを使うのか、それともアサーション以外の例外を使うのか、一般解は存在しない。例外設計における大罪や『Effective Java 2nd Edition』の「項目57: 例外的状態にだけ 例外を使用する」が参考にはなるけれど、最後は作ろうとしているソフトウェアがどう振る舞うべきか、自分で考えるしかなさそう。

というわけで、指針は掴めたけれど、簡単な話ではない。

References

2013年11月26日火曜日

FindBugsのDetector Pluginチュートリアルをやってみた

FindBugsのDetectorPluginTutorialを試してみたときのメモ。FindBugs Eclipse Pluginへの追加を想定している。使用したバージョンは次の通り。
  • Eclipse 4.2
  • FindBugs 2.0.2
以下、見出しごとに躓いたポイントをメモしておく。



Creating Our Plugin


Setting Up Eclipse

素直にそのままやって問題なかった。

Setting Up the Classpath

findbugs.jar, bcel.jarに加えて、jsr305.jarもClasspathに追加する必要があった。findbugs.jarが依存しているみたい。

Writing the Bug Detector

そのままDetector codeのサンプルをコピーして問題なかった。

Deployment & Testing

Building

以下2つのxmlファイルは、プロジェクトフォルダーのルートに配置する。書かれている通りだけれど、読み落としがち。

findbugs.xml

コメントにあるとおり、公式サイトと要素名が異なるけれど、こちらの記載通りFindBugsPlugin要素で問題なかった。

サンプルには含まれていないけれど、一行目にXML宣言を追加しておく方がベターだと思う。

messages.xml

そのままサンプルをコピーして問題なかった。

Building in Eclipse

そのままやれば問題ないのだけれど、JAR Exportの設定が色々と初期値と異なる点に注意。間違えるとプラグインとして追加しても、Invalidと表示されて機能しない。一度やれば、jardescからその設定で再ビルドできる。

CIするためにAntタスクとして定義したい。

Loading Our Plugin

GUI (Eclipseの設定画面 Java > FindBugs) から追加した。EclipseのpluginsフォルダのFindBugsのpluginフォルダに追加しても、うまくいくかも試したい。


ここまでやって、Quizのコードスニペットを含むクラスでFindBugsを実行してみたら、期待通り動作した。めでたし、めでたし。

以下は、覚え書き。

このチュートリアルの"Debugging"はまだ書かれていない。テストケースの簡単な説明はFindbugsTestCasesにあるけれど、ここを読むだけではちょっと分りそうにない。実際のテストケースを見ながら、試行錯誤が要りそう。

チュートリアルではなぜこうするのか分らないけれど、Plugins4FindBugsで補完できる。Detector Pluginの概要が記載されている。

Eclipse Pluginに特化した内容として、コメントでFindBugs In Eclipse Tutorialが紹介されているのに、後から気がついた。ただ、FindBugs 1.3.9よちょっと情報が古い。

References

2013年8月21日水曜日

Javaのデコンパイラと難読化ツール

Javaのクラスファイルをデコンパイルしたソースコードは読みやすいと聞いて、「どれくらいソースを再現できるのか?」と「難読化したらどれくらいデコンパイル結果が読み辛くなるのか?」について試してみたくなった。というわけで、まずはデコンパイラ (decompiler) と難読化ツール (obfuscator) ついて調べてみた。今回はツールの調査だけで、実行方法についてはおいおい。なお、調査方法は、GoogleとStack Overflow頼み。

デコンパイラはJava Decompilerでいいのかな。デコンパイラとして真っ先に思い浮かんだJADは、更新が止まっていた。Java DecompilerもEclipseプラグイン・JD-Eclipseが最近のEclipseだと動かないようだけれど、少なくとも、Mchr3k - JDEclipse-Realignというフォークがメンテナンスされている。
Java Decompiler (Yet another Fast Java decompiler) has:
  • explicit support for decompiling and analyzing Java 5+ “.class” files.
  • a nice GUI:
decompiler - How do I "decompile" Java class files? - Stack Overflow
ともあれ、こちらはすんなり使えた。クラスファイルからほぼソースそのままにデコンパイルできてビックリ。Live Demoもある。

難読化はProGuardが良さそう。予備知識が無いので手探りだけれど。こちらは現在もアクティブな様子。最近はAndroidアプリの難読化に力を入れているよう。
Well, you can find here a list. ProGuard is pretty good. I've used it myself, but only to "minify" Java code.
obfuscation - Best Java obfuscator? - Stack Overflow
ただ、こちらは簡単には使えなさそう。設定が必要そう。幸い公式サイトのマニュアルが充実しているようなので、それを見ながらもう少し調べよう。

ところで、難読化すると、クラスやメソッドなどの名前が変わるから、リフレクションAPIを使っていると実行時例外が送出される。けれど、ProGuardのIntroduction > Reflectionによると、典型的なリフレクションAPIに対応しているみたい。

References

2013年8月3日土曜日

Out parameter, collecting parameter, visitor pattern

『Effective Java (第二版)』の「項目15 可変性を最小限にする」では不変オブジェクト (Immutable object) を推奨している。これは状態を持たないから、シンプルに扱える。反対に状態を持つ可変オブジェクト (Mutable object) は、扱いに気を遣う。扱おうとしているときにどういう状態なのか意識しないといけない。状態に起因したバグがあったら、最悪、状態が変更されている場所を全て確認しないといけない。

これに真っ向から対立しているのが、Out parameter。状態を変えるために渡す可変オブジェクトのことを指す。Webを検索してみると、アンチパターンとして紹介されている記事が見つかる。

でも、"Collectiong parameter"として『実装パターン』や『パターン指向リファクタリング入門』で紹介されていたりする。また、『パターン指向リファクタリング入門』では、その延長線上に"Visitor Pattern"を適用したパターンも紹介されている。

確かにこちらの方が合理的なケースもありそう。でも、基本的にはあまり使わない方がよさそう。コマンドとクエリは分離するのが原則だ。使うなら、Javadocに明記するなりしないと混乱を呼びそう。C#のoutキーワードに相当するものがJavaにはないから、引数の状態を変えるかどうか宣言だけから読み取れない。

ところで、out (C#)に使用例として「メソッドが複数の値を返すようにする場合に便利」だなんて書かれている。同時に扱いたい複数の値があるなら、クラスとしてまとめた方が読みやすくないだろうか。原則として、入力を与えたら戻り値が返ってくる関数として考えたい。

References

2013年3月1日金曜日

インスタンスを生成するメソッドの名前

インスタンスを生成するメソッドの名前について、よく迷うのでまとめておく。

まずは、スタティックファクトリメソッド。"Effective Java (2nd Edition)"の"Item 2: Consider Static Factory Methods instead of Constructors"に倣って使うようにしているけれど、感覚で名前をつけてしまうことがあるので、気をつけないと。
  • valueOf: パラメータと同じ意味合いの値を持つインスタンスを返す。Integer#valueOf(int)はじめ、型変換メソッドが典型例。
  • of: valueOfと同じ。EnumSetで使われている。
  • getInstance: 指定したパラメータから組み立てたインスタンスを返す。Singletonパターンでよく使われる。
  • newInstance: getInstanceと同様だけれど、必ず新しいインスタンスを返す。だからSingletonパターンでは使えない。
  • getType: getInstanceと同様だけれど、ファクトリメソッドが他のクラスにある場合に使う。Typeは返値の型名で置き換えて使う。
  • newType: newInstanceと同様だけれど、ファクトリメソッドが他のクラスにある場合に使う。Typeは返値の型名で置き換えて使う。

続いて、インスタンスの型変換メソッド。つまりインスタンスを元に別のクラスのインスタンスを作っているメソッドの名前についてもまとめておく。いずれもTypeは変換後の型名で置き換えて使う。こちらは"Item 56: Adhere to Generally Accepted Naming Conventions"の一部。
  • toType: 異なった型の独立したオブジェクトを返す。例えば、Object#toString() 。
  • asType: そのオブジェクトの異なる型での表現を返す。adapter パターン (Effective Java ではView) で使う。例えば、Arrays#asList(T... a)。
  • typeValue: プリミティブ型を返す。例えば、Integer#intValue()。

References

2013年2月6日水曜日

Selenium 2.xで条件が満たされるまで待つ

Selenium 2.X (確認したのは2.28) で何か条件が満たされる(例えば、JavaScriptが実行されてロード時はクリックできない要素がクリックできるようになる)まで待つには、WebDriverWait#until(Predicate)を使う。
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("id")));
引数に渡しているインスタンスが条件を表している。よくある条件はExpectedConditionsクラスに用意されている。not(ExpectedCondition)もあるのでたいていの場合はこれで事足りる。

単に待つだけじゃなく、条件を満たした時のインスタンスを返してくれるのが便利。

独自の条件が必要な場合は、ExpectedCondition<T>インタフェースを実装して、applyメソッドをオーバーライドする。その返値の型は、インタフェースの型パラメータで指定する。nullでもfalseでもない値が返されると、条件を満たしたと判定される。

車輪の再発明だけれど、上記の例を無名クラスを使って実装すると、次のようになる。
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(new ExpectedCondition(){
      @Override
      public WebElement apply(WebDriver d) {
      return d.findElement(By.id("id"));
}});

References

Selenium 2.xでHTML要素の非存在をチェックする

Selenium 2.X (確認したのは2.28) で指定したHTML要素が存在しないことをチェックするには、WebDriver#findElements(By)を使う。返値 (List) の長さが0なら、存在しない。例えば、hogeクラスが指定されたdiv要素が存在しないことをチェックするなら、下記のようになる。

if (driver.findElements(By.cssSelector("div.hoge")).size() == 0) {
  // hogeクラスが指定されたdiv要素は存在しない
}
このことは、WebDriver#findElements(By)じゃなくてWebDriver#findElement(By)のJavadocに書いてある。ちょっと回りくどい上に、使うメソッドのJavadocには特に何も書いてなくて、よく見失う。
findElement should not be used to look for non-present elements, use findElements(By) and assert zero length response instead.
WebDriver#findElement(By)
1.XのAPIだと、Selenium#isElementPresent(String)というそのものズバリのメソッドがある。後方互換性があるから今でも動作するけれど、それを使うのにはパッと思いつくだけでデメリットが3つある。というわけで、なるべく2.XのAPIで統一していきたい。
  • 1.XのAPIは要素の指定で、バグを作りやすい ("locatorType=argument"フォーマットのStringで、"locatorType="の省略時動作に暗黙のルールがある)
  • APIのスタイルが混在すると、リーダビリティが下がる
  • 古いAPIはいつまでも使い続けられるとは限らない

References

2012年12月22日土曜日

ジェネリクスでAPIを柔軟にする

あるクラスの任意のサブクラスのリストを引数に取れるメソッドは、ジェネリクスの型引数にバインドされワイルドカードを指定すれば書ける。

当てずっぽうで書いたらダメだったから何とかその場しのぎのコードを書いていたのだけれど、その後読んだ"Effective Java 2nd Edition"の"Chapter 5 Generics"でいくぶんスッキリしたので整理してみる。

まずリストじゃない場合で前提の確認。Superクラスを継承するSubクラスがあるとき、どちらも引数に取れるメソッドはこう書ける。こう定義しておけば、どちらを渡しても大丈夫。
public void printInstance(Super instance) {
    System.out.println(instance);
}
printInstance(new Super());
printInstance(new Sub());

リストでも同じようにいけるかな? と思って、当てずっぽうで書いたダメな例がこれ。こう書いて、Subのリストを渡そうとすると、コンパイルエラーになる。実は、そもそも、List<Sub>はList<Super>のサブクラスではない。
public void printList(List<Super> list) {
    System.out.println(lst);
}
printList(new ArrayList<Sub>);

この場合はBounded wildcard type (バインドされたワイルドカードタイプ)を使って、こう書く。Superのサブクラスのリストだけじゃなくて、Superのリストも渡せる。
public void printList(List<? extends Super> list) {
    System.out.println(lst);
}

反対にあるクラスのスーパークラスのリストとそのクラス自身のリストを引数に取れるようにも書ける。その場合は、<? extends Super>の代わりに<? super Sub>を使う。

使い分けの指針には"PECS"や"get-put principle"がある。よくかみ砕けていないのだけれど、同じことを言っているように思う。こうしておくと型安全で使いやすいAPIになるとのこと。
PECS stands for producer-extends, consumer-super
"Effective Java 2nd Edition", Item 28: Use bounded wilidcards to increase flexibility
構造から値を取得する (get) だけの場合には extends ワイルドカードを使い、構造の中に値を格納する (put) だけの場合には super ワイルドカードを使い、そして両方を行う場合にはワイルドカードを使ってはなりません。
『Java の理論と実践: Generics のワイルドカードを使いこなす、第 2 回』

References