T-R*_*Rex 259 java inheritance
我是Java编程的新手,试图掌握OOP.
所以我构建了这个抽象类:
public abstract class Vehicle{....}
Run Code Online (Sandbox Code Playgroud)
和2个子类:
public class Car extends Vehicle{....}
public class Boat extends Vehicle{....}
Run Code Online (Sandbox Code Playgroud)
Car并且Boat还包含一些不常见的唯一字段和方法(不具有相同的名称,因此我无法在Vehicle中为它们定义抽象方法).
现在在mainClass我设置了我的新车库:
Vehicle[] myGarage= new Vehicle[10];
myGarage[0]=new Car(2,true);
myGarage[1]=new Boat(4,600);
Run Code Online (Sandbox Code Playgroud)
在我尝试访问Car独有的一个字段之前,我对多态性非常满意,例如:
boolean carIsAutomatic = myGarage[0].auto;
Run Code Online (Sandbox Code Playgroud)
编译器不接受.我使用cast来解决这个问题:
boolean carIsAutomatic = ((Car)myGarage[0]).auto;
Run Code Online (Sandbox Code Playgroud)
这有效......但它对方法没有帮助,只是字段.意思是我做不到
(Car)myGarage[0].doSomeCarStuff();
Run Code Online (Sandbox Code Playgroud)
所以我的问题是 - 我的车库里到底有什么?我试图获得直觉以及了解"幕后"发生了什么.
为了将来的读者,请简要总结以下答案:
Car在myGarage[]Vehicle myGarage[])Jea*_*art 147
如果您需要之间的区别Car,并Boat在你的车库,那么你应该将它们存储在不同的结构.
例如:
public class Garage {
private List<Car> cars;
private List<Boat> boats;
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以定义特定于船只或特定于汽车的方法.
让我们说Vehicle是这样的:
public abstract class Vehicle {
protected int price;
public getPrice() { return price; }
public abstract int getPriceAfterYears(int years);
}
Run Code Online (Sandbox Code Playgroud)
每个Vehicle都有一个价格,所以它可以放在Vehicle抽象类中.
然而,确定n年后的价格的公式取决于车辆,因此由实施类来定义它.例如:
public Car extends Vehicle {
// car specific
private boolean automatic;
@Override
public getPriceAfterYears(int years) {
// losing 1000$ every year
return Math.max(0, this.price - (years * 1000));
}
}
Run Code Online (Sandbox Code Playgroud)
该Boat班可能有一个其他的定义getPriceAfterYears和具体的属性和方法.
所以现在回到Garage课堂上,您可以定义:
// car specific
public int numberOfAutomaticCars() {
int s = 0;
for(Car car : cars) {
if(car.isAutomatic()) {
s++;
}
}
return s;
}
public List<Vehicle> getVehicles() {
List<Vehicle> v = new ArrayList<>(); // init with sum
v.addAll(cars);
v.addAll(boats);
return v;
}
// all vehicles method
public getAveragePriceAfterYears(int years) {
List<Vehicle> vehicules = getVehicles();
int s = 0;
for(Vehicle v : vehicules) {
// call the implementation of the actual type!
s += v.getPriceAfterYears(years);
}
return s / vehicules.size();
}
Run Code Online (Sandbox Code Playgroud)
多态的兴趣是能够getPriceAfterYears在Vehicle 不关心实现的情况下调用.
通常,向下倾斜是设计有缺陷的标志:如果您需要区分其实际类型,请不要将您的车辆全部存放在一起.
注意:当然这里的设计很容易改进.这只是一个展示要点的例子.
dan*_*max 84
要回答您的问题,您可以了解您的车库中究竟是什么,您可以执行以下操作:
Vehicle v = myGarage[0];
if (v instanceof Car) {
// This vehicle is a car
((Car)v).doSomeCarStuff();
} else if(v instanceof Boat){
// This vehicle is a boat
((Boat)v).doSomeBoatStuff();
}
Run Code Online (Sandbox Code Playgroud)
更新:正如你可以从下面的评论中读到的,这种方法对于简单的解决方案是可以的,但这不是一个好的做法,特别是如果你的车库里有大量的车辆.所以只有当你知道车库会保持小的时候才使用它.如果不是这种情况,请在堆栈溢出时搜索"避免instanceof",有多种方法可以执行此操作.
Ale*_*ühl 22
如果您对基本类型进行操作,则只能访问它的公共方法和字段.
如果你想访问扩展类型,但是有一个存储它的基本类型的字段(如你的情况),你首先必须强制转换它然后你可以访问它:
Car car = (Car)myGarage[0];
car.doSomeCarStuff();
Run Code Online (Sandbox Code Playgroud)
或者没有温度场时更短:
((Car)myGarage[0]).doSomeCarStuff();
Run Code Online (Sandbox Code Playgroud)
由于您正在使用Vehicle对象,因此您只能在不使用强制转换的情况下从基类调用方法.因此,对于你的车库,建议区分不同阵列中的对象 - 或更好的列表 - 数组通常不是一个好主意,因为它的处理灵活性远远低于Collection基于类的.
Ham*_*riZ 13
您定义了您的车库将存储车辆,因此您不关心您拥有什么类型的车辆.这些车辆具有发动机,车轮,移动等行为等共同特征.这些特征的实际表示可能不同,但在抽象层是相同的.你使用了抽象类,这意味着两种车辆的某些属性,行为完全相同.如果你想表达你的车辆有共同的抽象功能,那么使用移动界面可能意味着汽车和船只的不同.两者都可以从A点到达B点,但是以不同的方式(在轮子上或在水上 - 所以实施方式会有所不同)所以你在车库里的车辆行为方式相同而且你不关心具体功能他们
回答评论:
接口是指描述如何与外部世界通信的合同.在合同中,您定义您的车辆可以移动,可以操纵,但是您没有描述它将如何实际工作,它在实现中描述.通过抽象类,您可能具有共享某些实现的功能,但您也有你不知道它将如何实现的功能.
使用抽象类的一个例子:
abstract class Vehicle {
protected abstract void identifyWhereIAm();
protected abstract void startEngine();
protected abstract void driveUntilIArriveHome();
protected abstract void stopEngine();
public void navigateToHome() {
identifyWhereIAm();
startEngine();
driveUntilIArriveHome();
stopEngine();
}
}
Run Code Online (Sandbox Code Playgroud)
您将使用每辆车的相同步骤,但步骤的实施因车辆类型而异.汽车可能会使用GPS,船可能会使用声纳识别它的位置.
Syl*_*oux 13
我是Java编程的新手,试图掌握OOP.
仅仅是我的2美分 - 我会尝试缩短它,因为已经说过许多有趣的事情.但事实上,这里有两个问题.一个关于"OOP",一个关于如何在Java中实现它.
首先,是的,你的车库里有一辆车.所以你的假设是正确的.但是,Java是一种静态类型语言.编译器中的类型系统只能通过相应的声明 "知道"各种对象的类型.不是他们的用法.如果你有一个数组Vehicle,编译器只知道它.因此它会检查您是否只执行任何 操作Vehicle.(换句话说,声明中可见的方法和属性Vehicle).
您可以通过使用显式强制转换向编译器解释"您实际上知道这Vehicle是一个Car"(Car).编译器会相信你 - 即使在Java中运行时有一个检查,ClassCastException如果你撒谎 可能会导致阻止进一步损坏(其他语言如C++不会在运行时检查 - 你必须知道你做什么)
最后,如果你真的需要,你可能会依赖于运行时类型识别(即:) instanceof来检查对象的"真实"类型,然后再尝试强制转换它.但这在Java中被认为是一种不好的做法.
正如我所说,这是实现OOP的Java方式.有完全不同类 广泛称为"动态语言"的语言系列,仅在运行时检查是否允许对象进行操作.使用这些语言,您不需要将所有常用方法"向上移动"到某些(可能是抽象的)基类以满足类型系统.这叫做鸭子打字.
你问过你的管家:
Jeeves,还记得我在爪哇岛的车库吗?去检查停在那里的第一辆车是否是自动的.
和懒惰的Jeeves说:
但先生,如果这是一辆不能自动或非自动的车辆怎么办?
就这样.
好吧,这并不是全部,因为现实比静态类型更像是鸭子型.这就是我说Jeeves很懒的原因.
这里的问题处于一个更基础的层面:你构建Vehicle的方式Garage需要了解更多关于其对象的信息而不是Vehicle接口.你应该尝试Vehicle从Garage角度来构建这个类(一般来说,从所有将要使用的角度来看Vehicle):他们需要对他们的车辆做什么样的事情?如何用我的方法使这些事情成为可能?
例如,从您的示例:
bool carIsAutomatic = myGarage[0].auto;
Run Code Online (Sandbox Code Playgroud)
你的车库想知道车辆发动机的原因是什么?无论如何,没有必要让它暴露出来Car.您仍然可以在其中公开未实现的isAutomatic()方法Vehicle,然后return True在in Boat和return this.autoin中实现它Car.
它甚至会更好,有一个三值EngineType枚举(HAS_NO_GEARS,HAS_GEARS_AUTO_SHIFT,HAS_GEARS_MANUAL_SHIFT),这将让我们在一个通用的实际特点代码原因Vehicle干净,准确.(无论如何,你需要这种区别来处理摩托车.)
你的车库包含车辆,所以编译器静态控制视图你有一个车辆和.auto是一个你无法访问它的Car字段,动态它是一个Car所以演员不会产生一些问题,如果它将是一个船,你试图使铸造到汽车将在运行时上升异常.
这是应用Visitor设计模式的好地方.
这种模式的优点是你可以在超类的不同子类上调用不相关的代码,而不必在任何地方进行奇怪的转换或将大量不相关的方法放入超类中.
这可以通过创建一个Visitor对象并允许我们的Vehicle类accept()访问者来实现.
您还可以Visitor使用相同的方法创建许多类型的函数并调用不相关的代码,只是一个不同的Visitor实现,这使得这个设计模式在创建干净的类时非常强大.
一个演示例如:
public class VisitorDemo {
// We'll use this to mark a class visitable.
public static interface Visitable {
void accept(Visitor visitor);
}
// This is the visitor
public static interface Visitor {
void visit(Boat boat);
void visit(Car car);
}
// Abstract
public static abstract class Vehicle implements Visitable {
// NO OTHER RANDOM ABSTRACT METHODS!
}
// Concrete
public static class Car extends Vehicle {
public void doCarStuff() {
System.out.println("Doing car stuff");
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// Concrete
public static class Boat extends Vehicle {
public void doBoatStuff() {
System.out.println("Doing boat stuff");
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// Concrete visitor
public static class StuffVisitor implements Visitor {
@Override
public void visit(Boat boat) {
boat.doBoatStuff();
}
@Override
public void visit(Car car) {
car.doCarStuff();
}
}
public static void main(String[] args) {
// Create our garage
Vehicle[] garage = {
new Boat(),
new Car(),
new Car(),
new Boat(),
new Car()
};
// Create our visitor
Visitor visitor = new StuffVisitor();
// Visit each item in our garage in turn
for (Vehicle v : garage) {
v.accept(visitor);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,StuffVisitor允许您调用不同的代码Boat或Car根据调用的实现visit调用.您还可以创建访问者的其他实现,以使用相同的.visit()模式调用不同的代码.
另请注意,使用此方法时,不会使用instanceof任何hacky类检查.类之间唯一重复的代码是方法void accept(Visitor).
例如,如果要支持3种类型的具体子类,也可以将该实现添加到Visitor接口中.
我真的只是在这里汇集其他人的想法(我不是一个Java人,所以这是假的而不是实际的)但是,在这个人为的例子中,我会将我的汽车检查方法抽象为一个专门的类,在看车库时,只知道汽车,只关心汽车:
abstract class Vehicle {
public abstract string getDescription() ;
}
class Transmission {
public Transmission(bool isAutomatic) {
this.isAutomatic = isAutomatic;
}
private bool isAutomatic;
public bool getIsAutomatic() { return isAutomatic; }
}
class Car extends Vehicle {
@Override
public string getDescription() {
return "a car";
}
private Transmission transmission;
public Transmission getTransmission() {
return transmission;
}
}
class Boat extends Vehicle {
@Override
public string getDescription() {
return "a boat";
}
}
public enum InspectionBoolean {
FALSE, TRUE, UNSUPPORTED
}
public class CarInspector {
public bool isCar(Vehicle v) {
return (v instanceof Car);
}
public bool isAutomatic(Car car) {
Transmission t = car.getTransmission();
return t.getIsAutomatic();
}
public bool isAutomatic(Vehicle vehicle) {
if (!isCar(vehicle)) throw new UnsupportedVehicleException();
return isAutomatic((Car)vehicle);
}
public InspectionBoolean isAutomatic(Vehicle[] garage, int bay) {
if (!isCar(garage[bay])) return InspectionBoolean.UNSUPPORTED;
return isAutomatic(garage[bay])
? InspectionBoolean.TRUE
: InspectionBoolean.FALSE;
}
}
Run Code Online (Sandbox Code Playgroud)
重点是,当你询问汽车的变速器时,你已经决定只关心汽车了.所以请问CarInspector.感谢三态Enum,您现在可以知道它是自动的还是不是汽车.
当然,对于您关心的每辆车,您都需要不同的VehicleInspectors.而你刚刚推出了哪个VehicleInspector实例化链的问题.
相反,您可能希望查看接口.
摘要getTransmission到界面(例如HasTransmission).这样,您可以检查车辆是否有传输,或者写一个TransmissionInspector:
abstract class Vehicle { }
class Transmission {
public Transmission(bool isAutomatic) {
this.isAutomatic = isAutomatic;
}
private bool isAutomatic;
public bool getIsAutomatic() { return isAutomatic; }
}
interface HasTransmission {
Transmission getTransmission();
}
class Car extends Vehicle, HasTransmission {
private Transmission transmission;
@Override
public Transmission getTransmission() {
return transmission;
}
}
class Bus extends Vehicle, HasTransmission {
private Transmission transmission;
@Override
public Transmission getTransmission() {
return transmission;
}
}
class Boat extends Vehicle { }
enum InspectionBoolean {
FALSE, TRUE, UNSUPPORTED
}
class TransmissionInspector {
public bool hasTransmission(Vehicle v) {
return (v instanceof HasTransmission);
}
public bool isAutomatic(HasTransmission h) {
Transmission t = h.getTransmission();
return t.getIsAutomatic();
}
public bool isAutomatic(Vehicle v) {
if (!hasTranmission(v)) throw new UnsupportedVehicleException();
return isAutomatic((HasTransmission)v);
}
public InspectionBoolean isAutomatic(Vehicle[] garage, int bay) {
if (!hasTranmission(garage[bay])) return InspectionBoolean.UNSUPPORTED;
return isAutomatic(garage[bay])
? InspectionBoolean.TRUE
: InspectionBoolean.FALSE;
}
}
Run Code Online (Sandbox Code Playgroud)
现在你说,你只关于传输,不管是否有车辆,所以可以问一下TransmissionInspector.总线和汽车都可以通过TransmissionInspector进行检查,但它只能询问传输情况.
现在,您可能会认为布尔值并不是您所关心的.此时,您可能更喜欢使用通用的Supported类型,它公开支持的状态和值:
class Supported<T> {
private bool supported = false;
private T value;
public Supported() { }
public Supported(T value) {
this.isSupported = true;
this.value = value;
}
public bool isSupported() { return supported; }
public T getValue() {
if (!supported) throw new NotSupportedException();
return value;
}
}
Run Code Online (Sandbox Code Playgroud)
现在您的Inspector可能被定义为:
class TransmissionInspector {
public Supported<bool> isAutomatic(Vehicle[] garage, int bay) {
if (!hasTranmission(garage[bay])) return new Supported<bool>();
return new Supported<bool>(isAutomatic(garage[bay]));
}
public Supported<int> getGearCount(Vehicle[] garage, int bay) {
if (!hasTranmission(garage[bay])) return new Supported<int>();
return new Supported<int>(getGearCount(garage[bay]));
}
}
Run Code Online (Sandbox Code Playgroud)
正如我所说,我不是Java人,所以上面的一些语法可能是错的,但概念应该成立.尽管如此,如果不首先进