こんにちは!今日は、オブジェクトに関する非常に重要なトピックを見ていきます。ここでは、誇張せずに、この知識を実際の仕事で毎日使用することができると言えます。コンストラクターについて話します。この用語を初めて聞くかもしれませんが、実際にはコンストラクターを使用したことがあるかもしれませんが、自分でそれに気づいていなかっただけです :) これについては後で説明します。
Java のコンストラクターとは何ですか?なぜ必要ですか?
2 つの例を見てみましょう。public class Car {
String model;
int maxSpeed;
public static void main(String[] args) {
Car bugatti = new Car();
bugatti.model = "Bugatti Veyron";
bugatti.maxSpeed = 407;
}
}
車を作成し、そのモデルと最高速度を設定しました。ただし、実際のプロジェクトでは、 Carオブジェクトには明らかに 3 つ以上のフィールドがあります。たとえば、フィールドは 16 個です。
public class Car {
String model;//model
int maxSpeed;//max speed
int wheels;// disk width
double engineVolume;//engine capacity
String color;//color
int yearOfIssue;//year of issue
String ownerFirstName;//Owner's name
String ownerLastName;//owner's last name
long price;//price
boolean isNew;//new or not
int placesInTheSalon;//number of seats in the cabin
String salonMaterial;// interior material
boolean insurance;//is it insured
String manufacturerCountry;//manufacturer country
int trunkVolume;// trunk volume
int accelerationTo100km;//acceleration to 100 km/h in seconds
public static void main(String[] args) {
Car bugatti = new Car();
bugatti.color = "blue";
bugatti.accelerationTo100km = 3;
bugatti.engineVolume = 6.3;
bugatti.manufacturerCountry = "Italy";
bugatti.ownerFirstName = "Amigo";
bugatti.yearOfIssue = 2016;
bugatti.insurance = true;
bugatti.price = 2000000;
bugatti.isNew = false;
bugatti.placesInTheSalon = 2;
bugatti.maxSpeed = 407;
bugatti.model = "Bugatti Veyron";
}
}
新しいCarオブジェクトを作成しました。問題が 1 つあります。フィールドは 16 個ありますが、初期化したのは 12 個だけです。コードを使用して、忘れたものを見つけてみましょう。そう簡単ではありませんよね?このような状況では、プログラマは簡単に間違いを犯し、一部のフィールドの初期化をスキップする可能性があります。その結果、プログラムの動作が誤ったものになります。
public class Car {
String model;//model
int maxSpeed;//max speed
int wheels;// disk width
double engineVolume;//engine capacity
String color;//color
int yearOfIssue;//year of issue
String ownerFirstName;//Owner's name
String ownerLastName;//owner's last name
long price;//price
boolean isNew;//new or not
int placesInTheSalon;//number of seats in the cabin
String salonMaterial;// interior material
boolean insurance;//is it insured
String manufacturerCountry;//manufacturer country
int trunkVolume;// trunk volume
int accelerationTo100km;//acceleration to 100 km/h in seconds
public static void main(String[] args) {
Car bugatti = new Car();
bugatti.color = "blue";
bugatti.accelerationTo100km = 3;
bugatti.engineVolume = 6.3;
bugatti.manufacturerCountry = "Italy";
bugatti.ownerFirstName = "Amigo";
bugatti.yearOfIssue = 2016;
bugatti.insurance = true;
bugatti.price = 2000000;
bugatti.isNew = false;
bugatti.placesInTheSalon = 2;
bugatti.maxSpeed = 407;
bugatti.model = "Bugatti Veyron";
System.out.println("Model Bugatti Veyron. Engine size - " + bugatti.engineVolume + ", trunk - " + bugatti.trunkVolume + ", salon is made of" + bugatti.salonMaterial +
", disc width - " + bugatti.wheels + ". Was acquired in 2018 by Mr. " + bugatti.ownerLastName);
}
}
コンソール出力:
ブガッティ ヴェイロンモデル。排気量-6.3、トランク-0、内装はnull製、リム幅-0。nullさんより2018年に購入されました。
車に 200 万ドルを支払った購入者は、明らかに「ミスター ヌル」と呼ばれることを好まないでしょう。しかし真面目な話、結局のところ、私たちのプログラムは、リム幅が 0 (つまり、リムがまったくない) の車、トランクが欠落し、内装が未知の素材で作られ、さらには未知の誰かのものであるという、間違ったオブジェクトを作成することになりました。 。プログラムの実行中にこのようなエラーがどのように発生するかは想像するしかありません。このような状況を何とか回避する必要があります。プログラムには制限が必要です。たとえば、新しい車両オブジェクトを作成するときは、モデルと最高速度を常に指定する必要があります。それ以外の場合は、オブジェクトの作成を許可しません。コンストラクター関数はこのタスクに簡単に対処します。彼らには理由があってその名前が付けられました。コンストラクターはクラスの一種の「スケルトン」を作成し、クラスの新しい各オブジェクトはそれに対応する必要があります。便宜上、2 つのフィールドを持つ単純なバージョンのCarクラスに戻りましょう。要件を考慮すると、Carクラスのコンストラクターは次のようになります。
public Car(String model, int maxSpeed) {
this.model = model;
this.maxSpeed = maxSpeed;
}
オブジェクトの作成は次のようになります。
public static void main(String[] args) {
Car bugatti = new Car("Bugatti Veyron", 407);
}
注意してくださいコンストラクターがどのように作成されるか。これは通常のメソッドに似ていますが、戻り値の型がありません。この場合、クラス名はコンストラクター内でも大文字で示されます。私たちの場合 -車。さらに、コンストラクターは、初めてのキーワードthisを使用します。「this」は英語で「これ、これ」という意味です。この単語は特定のオブジェクトを指します。コンストラクター内のコード:
public Car(String model, int maxSpeed) {
this.model = model;
this.maxSpeed = maxSpeed;
}
ほぼ文字通りに翻訳できます: 「このマシンのモデル(現在作成中) =モデル引数、これはコンストラクターで指定されます。このマシンのmaxSpeed (作成中) = maxSpeed引数、これはコンストラクタで指定されています。」これが起こりました:
public class Car {
String model;
int maxSpeed;
public Car(String model, int maxSpeed) {
this.model = model;
this.maxSpeed = maxSpeed;
}
public static void main(String[] args) {
Car bugatti = new Car("Bugatti Veyron", 407);
System.out.println(bugatti.model);
System.out.println(bugatti.maxSpeed);
}
}
コンソール出力:
ブガッティ ヴェイロン 407
コンストラクターは必要な値を正常に割り当てました。コンストラクターが通常のメソッドと非常に似ていることに気づいたかもしれません。それはそのとおりです: コンストラクターはメソッドですが、少しだけ具体的です:) メソッドと同じように、パラメーターをコンストラクターに渡します。メソッドの呼び出しと同様に、コンストラクターの呼び出しも、指定しないと機能しません。
public class Car {
String model;
int maxSpeed;
public Car(String model, int maxSpeed) {
this.model = model;
this.maxSpeed = maxSpeed;
}
public static void main(String[] args) {
Car bugatti = new Car(); //error!
}
}
ご存知のとおり、デザイナーは私たちが達成しようとしていたことを実現してくれました。スピードがなければ、モデルがなければ車を作ることはできません。コンストラクターとメソッドの類似点はそれだけではありません。メソッドと同様に、コンストラクターもオーバーロードできます。あなたの家に2匹の猫がいると想像してください。そのうちの1匹を子猫のときに拾い、2匹目は成猫として路上から家に連れて帰りましたが、正確な年齢はわかりません。これは、プログラムでは 2 種類の猫 (最初の猫には名前と年齢があり、2 番目の猫には名前のみ) を作成できる必要があることを意味します。これを行うには、コンストラクターをオーバーロードします。
public class Cat {
String name;
int age;
//for the first cat
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
//for the second cat
public Cat(String name) {
this.name = name;
}
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5);
Cat streetCatNamedBob = new Cat("Bob");
}
}
パラメーター「name」と「age」を持つ元のコンストラクターに、名前のみを持つ別のコンストラクターを追加しました。前回のレッスンで同じ方法でメソッドをオーバーロードしました。これで、両方のバージョンの猫を正常に作成できるようになりました :) 講義の冒頭で、すでにコンストラクターを使用していたが、それに気付かなかっただけだと述べたのを覚えていますか? これは本当です。実際、Java のすべてのクラスには、いわゆるデフォルト コンストラクターがあります。引数はありませんが、任意のクラスのオブジェクトが作成されるたびに起動されます。
public class Cat {
public static void main(String[] args) {
Cat barsik = new Cat(); //this is where the default constructor worked
}
}
一見するとこれは目立ちません。さて、私たちはオブジェクトを作成して作成しましたが、デザイナーの仕事はどこにあるのでしょうか? これを確認するには、 Catクラスの空のコンストラクターを自分の手で作成し、その中でいくつかのフレーズをコンソールに出力してみましょう。表示されていれば、コンストラクターは機能しています。
public class Cat {
public Cat() {
System.out.println("Created a cat!");
}
public static void main(String[] args) {
Cat barsik = new Cat(); //this is where the default constructor worked
}
}
コンソール出力:
彼らは猫を作りました!
これが確認です!デフォルトのコンストラクターは常にクラス内に目に見えない形で存在します。しかし、もう 1 つ特徴を知っておく必要があります。引数を指定してコンストラクターを作成すると、デフォルトのコンストラクターがクラスから消えます。実際、この証拠はすでに上で見ました。このコードでは次のようになります。
public class Cat {
String name;
int age;
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Cat barsik = new Cat(); //error!
}
}
Cat のコンストラクター (文字列 + 数値) を定義したため、名前と年齢なしで猫を作成することはできませんでした。この直後、デフォルトのコンストラクターがクラスから消えました。したがって、クラス内に空のコンストラクターを含む複数のコンストラクターが必要な場合は、それを個別に作成する必要があることを必ず覚えておいてください。たとえば、動物病院向けのプログラムを作成しています。当院は、名前も年齢も分からない飼い主のいない猫たちを助けるため、善行を行いたいと考えています。コードは次のようになります。
public class Cat {
String name;
int age;
//for domestic cats
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
//for street cats
public Cat() {
}
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5);
Cat streetCat = new Cat();
}
}
デフォルトのコンストラクターを明示的に記述したので、両方のタイプの cat を作成できます :) コンストラクター (他のメソッドと同様) では、引数の順序が非常に重要です。コンストラクター内の name 引数と age 引数を交換してみましょう。
public class Cat {
String name;
int age;
public Cat(int age, String name) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 10); //error!
}
}
エラー!コンストラクターは、Cat オブジェクトの作成時に、数値と文字列をこの順序で渡す必要があることを明確に示しています。そのため、私たちのコードは機能しません。独自のクラスを作成するときは、必ずこれを覚えておいてください。
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public Cat(int age, String name) {
this.age = age;
this.name = name;
}
この二人は全く違うデザイナーなんです!「なぜコンストラクターが必要なのか?」という質問に対する答えを一文で表現すると、「オブジェクトが常に正しい状態になるため」と言えます。コンストラクターを使用すると、すべての変数が正しく初期化され、プログラム内に速度 0 の車やその他の「不正な」オブジェクトが存在しなくなります。これらを使用すると、まずプログラマ自身にとって非常に有益です。フィールドを自分で初期化する場合、何かを見逃したり、間違いを犯したりする危険性が高くなります。しかし、コンストラクターではこれは起こりません。コンストラクターに必要な引数をすべて渡さなかったり、型を混同したりすると、コンパイラーはすぐにエラーをスローします。プログラムのロジックをコンストラクター内に入れるべきではないことについては、別途言及する価値があります。これを行うには、必要なすべての機能を記述できるメソッドを自由に使用できます。コンストラクター ロジックがなぜ悪いアイデアなのかを見てみましょう。
public class CarFactory {
String name;
int age;
int carsCount;
public CarFactory(String name, int age, int carsCount) {
this.name = name;
this.age = age;
this.carsCount = carsCount;
System.out.println("Our car factory is called" + this.name);
System.out.println("She was founded" + this.age + " years ago" );
System.out.println("During this time it was produced" + this.carsCount + "cars");
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}
public static void main(String[] args) {
CarFactory ford = new CarFactory("Ford", 115 , 50000000);
}
}
車を生産する工場を記述するCarFactoryクラス があります。コンストラクター内で、すべてのフィールドを初期化し、ここにロジックを配置します。ファクトリに関する情報をコンソールに表示します。これには何の問題もないようで、プログラムは完璧に動作しました。コンソール出力:
私たちの自動車工場はフォードと呼ばれ、115 年前に設立され、この間に 5,000 万台の車を生産し、年間平均 434,782 台の車を生産しています。
しかし実際には、私たちは時限爆弾を仕掛けたのです。そして、そのようなコードは非常に簡単にエラーを引き起こす可能性があります。ここで、フォードの話ではなく、設立して 1 年も経たずに 1000 台の車を生産した新しい工場「アミーゴ モーターズ」について話していると想像してみましょう。
public class CarFactory {
String name;
int age;
int carsCount;
public CarFactory(String name, int age, int carsCount) {
this.name = name;
this.age = age;
this.carsCount = carsCount;
System.out.println("Our car factory is called" + this.name);
System.out.println("She was founded" + this.age + " years ago" );
System.out.println("During this time it was produced" + this.carsCount + "cars");
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}
public static void main(String[] args) {
CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
}
}
コンソール出力:
私たちの自動車工場は、スレッド "main" java.lang.ArithmeticException: / by zero で Amigo Motors Exception と呼ばれています。 0 年前に設立され、この間、CarFactory.<init>(CarFactory.java:15) で 1000 台の車を生産しました。 CarFactory.main(CarFactory.java:23) プロセスは終了コード 1 で終了しました</init>
到着しました!プログラムは奇妙なエラーで終了しました。何が原因なのか推測してみませんか?その理由は、コンストラクターに配置したロジックにあります。具体的には、この行では次のようになります。
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
ここで計算を行い、生産された自動車の数を工場の築年数で割ります。そして、私たちの工場は新しい(つまり築0年)ため、結果は数学では禁止されている0による除算になります。その結果、プログラムはエラー終了します。私たちは何をすべきだったのでしょうか?すべてのロジックを別のメソッドに移動し、それを呼び出します (例: printFactoryInfo() )。CarFactoryオブジェクトをパラメータとして渡すことができます。また、すべてのロジックをそこに配置し、同時に、ゼロ年のエラーのように、考えられるエラーを処理することもできます。それぞれ自分自身に。コンストラクターは、オブジェクトの状態を正しく設定するために必要です。ビジネス ロジックにはメソッドがあります。どちらかを混ぜてはいけません。
GO TO FULL VERSION