Java数据类替代方案有哪些?租车系统computeCost方法如何优化重构?
Hi there, let's tackle your two questions one by one:
Java doesn't have built-in data classes like Kotlin, but there are several solid alternatives depending on your project's needs:
Lombok的
@Data注解
这是最流行的减少样板代码的方案。Lombok会自动为你生成getter、setter、equals()、hashCode()、toString()甚至构造方法。示例:import lombok.Data; @Data public class Car { private String model; private int dailyRate; }它和Kotlin的data class效果几乎一致,能帮你省去大量重复代码的编写工作。
手动编写样板代码
如果项目不允许引入第三方库,那你就得自己实现getter、setter、equals()、hashCode()和toString()。虽然繁琐,但能让你完全控制每个方法的行为,适合对依赖有严格要求的场景。Java 16+的Record类
Record是Java官方推出的不可变数据类解决方案,它会自动生成equals()、hashCode()、toString()和访问器方法(注意:没有get前缀)。示例:public record Rental(int durationDays, int mileage, Car car) {}Record是不可变的(所有字段都是
final),非常适合只需要存储数据、不需要后续修改的场景。Google AutoValue
另一个专注于不可变性的代码生成库,通过注解处理器为抽象类生成实现。示例:import com.google.auto.value.AutoValue; @AutoValue public abstract class Car { public abstract String model(); public abstract int dailyRate(); public static Car create(String model, int dailyRate) { return new AutoValue_Car(model, dailyRate); } }如果你想要不可变数据类但又不想用Lombok,这会是个不错的选择。
你的痛点完全可以理解——在computeCost里硬编码车型判断的if-else既混乱又难维护,把方法移到车型子类也不合适,因为计算逻辑依赖的是租赁时长、里程这些租赁专属数据。这里有几个简洁的解决方案:
方案1:枚举实现策略模式(适合固定车型场景)
既然你有固定数量的车型(可能后续新增),可以用枚举来封装费率逻辑:
首先定义一个获取费率的接口:
public interface RateProvider { int getDailyRate(); }
然后让枚举实现这个接口,每个枚举值对应一种车型:
public enum CarType implements RateProvider { ECONOMY(50), SUV(100), LUXURY(200); private final int dailyRate; CarType(int dailyRate) { this.dailyRate = dailyRate; } @Override public int getDailyRate() { return dailyRate; } }
现在更新Rental类,再也不需要if-else判断了:
public class Rental { private int durationDays; private int mileage; private CarType carType; // 构造器、getter、setter public double computeCost() { // 可以根据实际定价规则调整公式 int dailyRate = carType.getDailyRate(); return dailyRate * durationDays + (mileage * 0.5); } }
新增车型只需要在枚举里加一个值就行,完全不用修改computeCost方法,符合开闭原则。
方案2:可配置的费率映射表(适合动态费率场景)
如果费率可能动态变化(比如从数据库或配置文件加载),可以用Map来存储车型和费率的映射关系:
创建一个费率配置类:
public class RateConfig { private static final Map<String, Integer> CAR_RATES = new HashMap<>(); static { // 初始化默认费率,也可以从配置文件/数据库加载 CAR_RATES.put("ECONOMY", 50); CAR_RATES.put("SUV", 100); CAR_RATES.put("LUXURY", 200); } public static int getDailyRate(String carType) { // 可以添加错误处理,比如对未知车型抛出异常 return CAR_RATES.getOrDefault(carType, 0); } }
然后在Rental类中使用:
public double computeCost() { int dailyRate = RateConfig.getDailyRate(this.carType); return dailyRate * durationDays + (mileage * 0.5); }
这种方式可以让你不用修改代码就能更新费率——只需要修改映射表的数据源(配置文件、数据库等)。
方案3:将Car作为值对象
如果你的车型需要携带除费率外的更多属性,可以把Car做成一个值对象,让它自己持有费率:
// 可以用Lombok的@Data来避免写样板代码 public class Car { private String model; private int dailyRate; public Car(String model, int dailyRate) { this.model = model; this.dailyRate = dailyRate; } // Getter public int getDailyRate() { return dailyRate; } }
然后Rental类直接使用Car对象的费率即可:
public class Rental { private int durationDays; private int mileage; private Car car; // 构造器、getter public double computeCost() { return car.getDailyRate() * durationDays + (mileage * 0.5); } }
这样Rental类专注于租赁逻辑,Car类负责自身属性,非常适合车型有多个关联属性的场景。
以上所有方案都能消除混乱的if-else判断,让代码更干净、更易维护,也更便于扩展。
内容的提问来源于stack exchange,提问作者User678

