为类初始化具有常数值的通用HashMap



我在实例化具有常数值的通用HashMap时遇到问题。我打算跟踪汽车租赁服务中各种汽车类型的库存;汽车类型作为钥匙,可供出租的数量作为价值。我尝试使用createAvailable cars方法,该方法将映射初始化为每个汽车类型的最大数量的常量。为了进一步测试,我还包含了一个setMaxCarsAvailable方法。尽管如此,我还是从我的canReserveVehicle方法中得到了一个NullPointer异常,在这里的行中输入图像描述,指定如果有0辆可用的汽车,那么你就不能预订车辆。如何使用我的汽车地图正确处理库存?我应该把它放在哪里?我尝试使用一个静态方法,后来把它包含在构造函数中,但没有成功。请参阅下面的代码。。(我已经在testCase类中包含了一张显示错误的堆栈跟踪图片。我希望所有这些额外的信息都有帮助)

public class CarReservationController {
String phoneNumber; 
long numDays = 0; 
Vehicle vehicle;
VehicleType vType;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 
public static final int MAX_ECONOMY = 10; //used this to track the amount of cars available to rent. This was applied in the canReserveVehicle and addReservation methods
public static final int MAX_PREMIUM = 10; 
public static final int MAX_SUV = 5;
public CarReservationController()
{
availableCars = createAvailableCarsMap(); //this is my attempt at instantiating my availableCars map to contain (VehicleType.ECONOMY, 10), (VehicleType.PREMIUM, 10), map.put(VehicleType.SUV, 5); ;
}
Map<VehicleType, Integer> availableCars; 
Map<VehicleType, PriorityQueue<Date>> reservedVehicleReturnDates = new HashMap<>(); // Map from vehicle type to reserved car end dates. This will hold all the current reservations end dates for each vehicle type
//was public static map
public HashMap<String, List<CarReservation>> reservationsMap = new HashMap<>();
//previously private static Map... 
private Map<VehicleType, Integer> createAvailableCarsMap() {
Map<VehicleType, Integer> map = new EnumMap<VehicleType, Integer>(VehicleType.class);
map.put(VehicleType.ECONOMY, MAX_ECONOMY);
map.put(VehicleType.PREMIUM, MAX_PREMIUM);
map.put(VehicleType.SUV, MAX_SUV);
return map;
}

public void setMaxCarsAvailable(VehicleType v, int maxAvailable) {
availableCars.put(v, maxAvailable);
}
//I UPDATE carReservationsMap here..this adds an actual reservation but first it checks the boolean canReserveVehicle below
public void addReservation(CarReservation res) throws Exception //right here
{   
Date sDate = res.getStartDate(); //HERE
Date eDate = res.getEndDate(); //HERE
String phoneNumber = res.getPhoneNumber();
if(canReserveVehicle(vType, phoneNumber, sDate, eDate)) {
if (reservationsMap.containsKey(phoneNumber)) {
List<CarReservation> currCustomerRes = reservationsMap.get(phoneNumber);
currCustomerRes.add(res);
reservationsMap.put(phoneNumber, currCustomerRes);
} else {
List<CarReservation> currCustomerRes = new ArrayList<CarReservation>(Arrays.asList(res));
reservationsMap.put(phoneNumber, currCustomerRes);
}
int countForVehicleType = availableCars.get(vType);
availableCars.put(vType, countForVehicleType - 1);
if (reservedVehicleReturnDates.containsKey(vType)) {
reservedVehicleReturnDates.get(vType).add(eDate);
} else {
PriorityQueue<Date> q = new PriorityQueue<Date>();
reservedVehicleReturnDates.put(vType, q);
}
}   
}
//NULL POINTER EXCEPTION COMING UP HERE FROM JUNIT TESTS
public boolean canReserveVehicle(VehicleType v, String phoneNumber, Date startDate, Date endDate) throws ParseException 
{
PriorityQueue<Date> reservedVehicleQueue = reservedVehicleReturnDates.get(v);
if(endDate.before(startDate))
return false; // check that the start date of the reservation is before the end date 
if (availableCars.get(v) == 0) { /// SAYS THERE IS A NULL POINTER EXCEPTION from here... because availableCars is still 0..
Date nextCarReturnDate = reservedVehicleQueue.peek();
if(nextCarReturnDate.after(startDate))
return false; // return false if a reserved car is not going to be available before the new customer is requesting one.
}
else {
// If a car that will become available before the customer requests it, remove it from the queue and replace with the 
//requesting customer's return date (as they now lay claim to the car)
reservedVehicleQueue.poll();
reservedVehicleQueue.add(endDate);
}
//these are comparing strings.
if (reservationsMap.containsKey(phoneNumber)) {
List<CarReservation> resByCustomer = reservationsMap.get(phoneNumber);
CarReservation lastResByCustomer = resByCustomer.get(resByCustomer.size() - 1); 
Date lastResEndDate = sdf.parse(lastResByCustomer.endDate);
if (startDate.before(lastResEndDate)) {  //1 customer can only have one rental at a time within the system.
return false;
} 
}
return true;    
}

}

"java.lang.NullPointerException"CarReserveController.canReserveCarVehicle""的测试用例如下所示

import java.text.SimpleDateFormat;
import org.junit.Test;
public class CarReservationTest {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 

@Test
public void testThatCustomerCanMakeReservation() throws Exception {
CarReservationController reservationSystem = new CarReservationController();
reservationSystem.setMaxCarsAvailable(VehicleType.PREMIUM, 2);
CarReservation firstRes = new CarReservation(VehicleType.PREMIUM, "Jon Snow", "1234567890", "2019-01-23", "2019-01-31");
reservationSystem.addReservation(firstRes);
//assertTrue(reservationSystem.reservationsMap.containsKey("1234567890"));
assertTrue(reservationSystem.reservationsMap.size() > 0);
assertEquals(firstRes, reservationSystem.reservationsMap.get("1234567890"));
}
}

有几个问题使调试变得复杂。

也许对于你问的问题来说,最重要的是,在没有完整的堆栈竞争的情况下,你看到的NPE是来自availbleCars.get(v)还是来自availableCars.get(v) == 0并不明显。

在不知道ReservationSystem::addReservation方法的作用的情况下,我认为不可能排除这两种可能性,这一事实使这个问题变得复杂。

可能性1

但是,如果问题来自availableCars.get(v) == 0,那么您可以选择使用==而不是.equals()来比较Integer和数字基元之间的相等性。请参阅前面的答案以获得更完整的讨论:为什么在Java中比较Integer和int会引发NullPointerException?

可能性2

如果问题来自availableCars.get(v)(availableCars本身就是null),那么您可能对如何实例化availableCars映射有问题。您在那里使用的方法不需要是静态的,也不需要您创建的setter。

下一步

为了解决这个问题,我建议使用断点,或者用调试语句将比较分为两个步骤——首先检查availableCars是否为空,然后检查availableCars.get(v)是否为Integer,然后使用.equals()检查与0的相等性。

此外,您可以尝试对方法进行单元测试,以便分别实例化availableCars映射和ReservationSystem::addReservation方法,以帮助缩小错误的范围

最新更新