我有5或6个类,我想在内部遵循相同的基本结构。真正大多数类应该遵循的只是函数本身的使用,所以我真的希望这些方法是私有的。
有没有办法实现这个目标?我知道接口可以很好地工作,但它们不会占用私有成员,也不会允许您重新定义已实现方法中的作用域。有没有解决方法呢?
由于
答案 0 :(得分:16)
我认为您最接近的方法是使用abstract
类和abstract
protected
方法:
abstract class A {
protected abstract void foo();
}
class B extends A {
protected void foo() {}
}
要定义公共逻辑,可以从超类中的私有方法调用protected方法:
abstract class A {
private void bar() {
// do common stuff
foo();
}
protected abstract void foo();
}
这样,您可以允许子类使用特定行为填充私有公共template method。
答案 1 :(得分:6)
创建一个概述结构和公共流程的抽象基类。为流程中必须由继承类实现的步骤指定抽象方法。
答案 2 :(得分:5)
嗯,任何其他类even by subclasses都无法调用私有函数。那么在不同的类中使用相同名称的私有函数有什么意义呢?
答案 3 :(得分:3)
在编译时无法强制执行它,但您可以编写单元测试或简单程序来测试使用反射的方法是否存在。
我认为你这样做是为了使课程符合美学/设计原因。如果你出于其他原因这样做,你应该真正使用其他人建议的抽象保护方式。
这里有一些代码可以让你开始进行这样的工具/单元测试(至少你应该改进错误消息,我真的建议单元测试,而不是我在这里有什么):
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Main
{
public static void main(String[] args)
{
check(B.class, Modifier.PRIVATE, void.class, "doit", new Class<?>[] { int.class });
check(C.class, Modifier.PRIVATE, void.class, "doit", new Class<?>[] { int.class });
}
private static void check(final Class<?> clazz,
final int modifiers,
final Class<?> returnType,
final String name,
final Class<?>[] params)
{
try
{
final Method method;
method = clazz.getDeclaredMethod(name, params);
if(method.getModifiers() != modifiers)
{
System.out.println("modifiers do not match");
}
if(method.getReturnType() != returnType)
{
System.out.println("return type does not match");
}
}
catch(final NoSuchMethodException ex)
{
System.out.println("could not find method");
}
}
}
interface A
{
void foo();
}
class B
implements A
{
public void foo()
{
doit(0);
}
private void doit(final int x)
{
}
}
class C
implements A
{
public void foo()
{
doit(0);
}
private int doit(final int x)
{
return (5);
}
}
答案 4 :(得分:2)
创建一个大纲'common'类,其中包含所有私有方法。
然后创建5或6个类,每个类都有一个类型为'common'的字段。 当然,你无法调用私有方法(但是你说这些方法实际上是内部的) - 你当然也必须做一些公共方法来改变状态。
public class common {
private method1() { ; }
private method2() { ; }
public other() { ; }
...
}
public class myclass1 {
common commonMethods;
}
public class myclass2 {
common commonMethods;
}
或甚至(假设'common'定义如上):
public class template {
common commonMethods;
}
public class myclass1 extends template {
...
}
所以你在5或6个子类的每一个上得到一个(包受保护的)'commonMethods'字段,用于'free'。
在对此线程进行后续讨论后,似乎作者实际上并不想共享逻辑:只是方法签名本质上,所以这个答案不符合该要求。
答案 5 :(得分:2)
虽然接口方法本身必须始终是公共的,但您可以将接口包设为私有,并将所有Car(例如)实现保留在同一个包中。
package com.some.car.pkg;
interface Car
{
public void gas();
public void brake();
}
即使这些方法是公开的,也没关系,因为在com.some.car.pkg包之外,Car不可见。这样,您的所有实现者都不会被迫扩展抽象类。你想要通用方法意味着真正的私有并不是真正的解决方案,恕我直言,你想要一个接口,因为听起来像你的情况,抽象类不是很正确,因为没有共享逻辑。
我的2美分。
答案 6 :(得分:1)
使用标记为final的方法创建一个抽象基类,该方法描述包含私有方法的公共流。将其标记为final意味着它不能通过子类进行扩展,因此只要您的调用代码使用它,就会强制执行业务逻辑。可以通过将方法标记为受保护来创建扩展点。例如,假设您有一个代表零售商店的类。
private final void doTransaction() {
float amountDue;
// a protected or abstract method that extenders can override
Collection items = this.unloadShoppingCart();
for (Object item : items) {
// another protected or abstract method
amountDue += this.getPrice(item);
}
// your private method
amountDue += this.getSalesTax(amountDue);
}
答案 7 :(得分:1)
“throw MethodNotImplementedException();”可能是一个有用的结构。
答案 8 :(得分:1)
如果抽象保护确实没有得到足够的保护,我想知道关注的是什么。在任何情况下,类似于monojohnny的替代方案是使用策略模式。这可以确保:
例如,尽管没有汽车印章,但是借用汽车比喻道歉:
public interface GearBoxStrategy {
public void changeGear(int newGear);
}
abstract public class Car {
private GearBoxStrategy gearBox;
public Car(GearBoxStrategy g) {
this.gearBox = g;
}
public void accelerate(double targetSpeed) {
int gear = getTargetGear(targetSpeed):
gearBox.shift(gear);
}
}
public class AutomaticTransmissionCar {
public AutomaticTransmissionCar() {
super(new AutomaticTransmissionGearBoxStrategy());
}
}
public class ManualTransmissionCar {
public ManualTransmissionCar() {
super(new ManualTransmissionGearBoxStrategy());
}
}
答案 9 :(得分:1)
看看XDepend,它使用反射根据编译的代码创建数据库。
它的目标是那些希望能够快速检查潜在大型编译代码库以寻找潜在问题区域的软件架构师。它有内置的报告和可视化,如类之间的关系,圈复杂度,耦合等等。
此外,它还包括一个内置的sql,如查询语言“CQL”(用于“代码查询语言”)。使用CQL,您可以定义自己的报告。您可能应该能够使用它来定义违反所描述规则的报告。此外,您可以使用注释将CQL查询直接嵌入到代码中。
我没有调查过,但是使用了它的.NET等效的'NDepend',这是一个非常酷的工具。
当然,您也可以编写自己的自定义工具,该工具使用反射来检查您的特定规则。 XDepend可能仍然值得一看 - 它应该更加灵活。
答案 10 :(得分:1)
是否可以使所有类继承自同一个基类?
如果是这样,你可以考虑的一件事就是 在运行时基类的构造函数中使用反射来验证子类是否遵循你描述的规则 ,并抛出如果它未通过验证规则,则为例外。
这个测试的天真实现当然会产生严重的性能问题,所以你必须非常聪明地实现测试。
首先,对于特定子类型T的所有实例,只应运行一次测试。因此,您必须在某处缓存验证信息。一种方法是在基类中使用某种静态(全局)哈希表,键入每个子类型的类型。
您还必须围绕此缓存执行某种线程安全同步。你真正需要避免的是读取的性能损失。我之前在类似情况下所做的是使用双重检查锁定模式和使用不可变哈希表的组合,这样在尝试写入哈希表时(即当您创建哈希表时)只会锁定性能特定子类型T)的第一个实例。
我实际上没有Java经验,我描述的是,我在.NET中实现,这就是为什么我无法为您提供代码示例,但所有概念都应该可以轻松转移到Java - 我提到的一切是(AFAIK)可在两个平台上使用。
答案 11 :(得分:0)
考虑使用抽象基类。这是一篇陈旧但相关的文章:http://www.javaworld.com/javaworld/javaqa/2001-04/03-qa-0420-abstract.html
答案 12 :(得分:0)
这是一个想法:编写一个简单的文本解析器来检查方法的存在。将其作为Ant中的任务包含在内。只要你坚持某种形式的编码标准,一些简单的文本匹配就应该这样做,即只需在所需的源文件中查找格式化的签名。
答案 13 :(得分:0)
在评论中你写道“是的,这是重点。我知道它们可以被称为不同的东西,但我不希望它们成为。”
现在,有些人可能会说“那是不可能的”,但就像编程中的大多数事情一样,它实际上 不可能,这只是很多工作。
如果您真的想这样做,可以为您的班级创建自定义Java Annotation,然后编写Annotation processor并在构建过程中调用apt。
就像我说了很多工作一样,但如果你想了解Annotations的工作方式,那可能是值得的。
编写注释实际上非常简单。他们的工作方式类似于普通班级。例如,如果您只是出于某种原因想要标记一个类,则可以创建一个空的标记注释,如下所示
public @interface Car { }
然后在您的注释处理器中,您可以检查以确保Car具有正确的私有方法。
我已经编写了自己的注释,但是我在运行时使用反射API检查了它们,而不是在构建时。它们实际上非常简单。