Java中消除fillRect方法重复代码的最优方案咨询
看了你写的两个fillRect方法,确实有大量重复的边界校验逻辑,Java没有指针确实会让这类需要同时修改多个变量的场景有点棘手,但其实有几种既简洁又低开销的方案可以解决这个问题:
方案1:使用Java 16+的Record类(推荐)
Java 16引入的Record是专门用来承载不可变数据的轻量级类型,完全不用担心内存或CPU开销——JVM会对它做充分优化,而且代码可读性极强。
首先定义一个用于存储调整后坐标和尺寸的Record:
private record BoundaryAdjustment(int coord, int size) {}
然后把重复的边界校验逻辑提取到一个私有方法里,返回这个Record:
private BoundaryAdjustment validateBoundary(int initialCoord, int initialSize, int maxDimension) { int coord = initialCoord; int size = initialSize; // 你的边界校验逻辑 if (coord + size > maxDimension) { size -= coord + size - maxDimension; } if (coord < 0) { size += coord; coord = 0; } return new BoundaryAdjustment(coord, size); }
接下来改造你的fillRect方法,调用这个校验方法即可:
public void fillRect(float x, float y, float width, float height, Color color) { BoundaryAdjustment horizontal = validateBoundary(mapX(x), mapWidth(width), pixelWidth); BoundaryAdjustment vertical = validateBoundary(mapY(y), mapHeight(height), pixelHeight); int xi = horizontal.coord(); int widthf = horizontal.size(); int yi = vertical.coord(); int heightf = vertical.size(); for (int xx = xi; xx < xi + widthf; xx++){ for (int yy = yi; yy < yi + heightf; yy++){ setPixel(xx, yy, color); } } } public void fillRect(float x, float y, float width, float height, float transparency, Color color) { BoundaryAdjustment horizontal = validateBoundary(mapX(x), mapWidth(width), pixelWidth); BoundaryAdjustment vertical = validateBoundary(mapY(y), mapHeight(height), pixelHeight); int xi = horizontal.coord(); int widthf = horizontal.size(); int yi = vertical.coord(); int heightf = vertical.size(); for (int xx = xi; xx < xi + widthf; xx++){ for (int yy = yi; yy < yi + heightf; yy++) { plot(xx, yy, transparency, color); } } }
这种方式既消除了重复代码,又保持了代码的清晰性,而且Record的开销几乎可以忽略——比起你循环中大量的像素操作,这点成本完全不值一提。
方案2:用数组作为“可变容器”(兼容旧Java版本)
如果你的项目还在用Java 16之前的版本,可以用长度为1的int数组来模拟“可变引用”,因为数组是引用类型,方法内部修改数组元素会影响外部。
先写校验方法:
private void validateBoundary(int[] coordRef, int[] sizeRef, int maxDimension) { int coord = coordRef[0]; int size = sizeRef[0]; // 边界校验逻辑 if (coord + size > maxDimension) { size -= coord + size - maxDimension; } if (coord < 0) { size += coord; coord = 0; } // 把修改后的值写回数组 coordRef[0] = coord; sizeRef[0] = size; }
调用的时候:
public void fillRect(float x, float y, float width, float height, Color color) { int[] xiRef = {mapX(x)}; int[] widthfRef = {mapWidth(width)}; int[] yiRef = {mapY(y)}; int[] heightfRef = {mapHeight(height)}; validateBoundary(xiRef, widthfRef, pixelWidth); validateBoundary(yiRef, heightfRef, pixelHeight); int xi = xiRef[0]; int widthf = widthfRef[0]; int yi = yiRef[0]; int heightf = heightfRef[0]; // 后续循环逻辑不变... }
这种方式的开销比Record还小,唯一的缺点是可读性稍差,因为数组元素的含义需要靠注释或变量名来明确,但对于简单场景完全够用。
方案3:自定义轻量内部类(兼容旧版本,可读性更好)
如果觉得数组不够直观,可以写一个极简的内部类来承载结果,比如:
private static class BoundaryResult { public int coord; public int size; public BoundaryResult(int coord, int size) { this.coord = coord; this.size = size; } }
然后校验方法返回这个类的实例,用法和Record几乎一样,只是需要自己写类的代码。这种方式的开销也非常小,JVM会对这类小类做优化,不用担心性能问题。
为什么不用POJO?
其实你担心的POJO开销是多余的——对于这种只承载两个int值的小对象,JVM会用逃逸分析把它分配在栈上(而不是堆),甚至可能直接把对象拆解成单独的变量,完全不会有额外的内存或性能负担。而且Record本质上就是一种特殊的POJO,只是Java帮你自动生成了构造方法、equals、hashCode等方法而已。
总结一下,优先推荐用Record(Java 16+),兼顾可读性和性能;如果是旧版本,用内部类或数组都可以,根据团队的代码风格选择就行。
内容的提问来源于stack exchange,提问作者Meeresgott

