首先是一些上下文:下面粘贴的所有代码都在另一个声明为public class TheClass extends SomeProprietaryClass
的类中。我不能出于各种原因在另一个文件中声明这些类...并且日志消息是法语。而且我是一个“最后快乐”的程序员。这是问题的核心......
现在,代码......(可能太多了 - 按要求剥离只保留相关部分)
自定义例外:
private static final class BreadCrumbException
extends Exception
{
private BreadCrumbException(final String message)
{
super(message);
}
private BreadCrumbException(final String message, final Throwable cause)
{
super(message, cause);
}
}
用于“实现”面包屑元素可见性的枚举:
private enum Visibility
{
MAINPAGE("R"),
MENU("M"),
BREADCRUMB("A"),
COMMERCIAL("C");
private static final Map<String, Visibility> reverseMap
= new HashMap<String, Visibility>();
private static final String characterClass;
static {
final StringBuilder sb = new StringBuilder("[");
for (final Visibility v: values()) {
reverseMap.put(v.flag, v);
sb.append(v.flag);
}
sb.append("]");
characterClass = sb.toString();
}
private final String flag;
Visibility(final String flag)
{
this.flag = flag;
}
static EnumSet<Visibility> fromBC(final String element)
{
final EnumSet<Visibility> result = EnumSet.noneOf(Visibility.class);
for (final String s: reverseMap.keySet())
if (element.contains(s))
result.add(reverseMap.get(s));
return result;
}
static String asCharacterClass()
{
return characterClass;
}
static String asString(final EnumSet<Visibility> set)
{
final StringBuilder sb = new StringBuilder();
for (final Visibility v: set)
sb.append(v.flag);
return sb.toString();
}
@Override
public String toString()
{
return flag;
}
}
面包屑元素:
private static class BreadCrumbElement
{
private static final Pattern p
= Pattern.compile(String.format("(%s+)(\\d+)",
Visibility.asCharacterClass()));
private final String element;
private final String menuID;
private final EnumSet<Visibility> visibility;
BreadCrumbElement(final String element)
{
final Matcher m = p.matcher(element);
if (!m.matches())
throw new IllegalArgumentException("Élément de fil d'ariane invalide: " + element);
this.element = element;
visibility = EnumSet.copyOf(Visibility.fromBC(m.group(1)));
menuID = m.group(2);
}
public boolean visibleFrom(final Visibility v)
{
return visibility.contains(v);
}
@Override
public boolean equals(final Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final BreadCrumbElement that = (BreadCrumbElement) o;
return element.equals(that.element);
}
@Override
public int hashCode()
{
return element.hashCode();
}
@Override
public String toString()
{
return element;
}
public String getMenuID()
{
return menuID;
}
}
面包屑:
private static class BreadCrumb
implements Iterable<BreadCrumbElement>
{
private static final BreadCrumb EMPTY = new BreadCrumb();
private final List<BreadCrumbElement> elements
= new LinkedList<BreadCrumbElement>();
private String bc;
BreadCrumb(final String bc)
throws BreadCrumbException
{
final Set<BreadCrumbElement> set = new HashSet<BreadCrumbElement>();
BreadCrumbElement e;
for (final String element: bc.split("\\s+")) {
e = new BreadCrumbElement(element);
if (!set.add(e))
throw new BreadCrumbException("Élément dupliqué "
+ "dans le fil d'Ariane : " + element);
elements.add(e);
}
if (elements.isEmpty())
throw new BreadCrumbException("Fil d'ariane vide!");
if (!elements.get(0).visibleFrom(Visibility.MAINPAGE))
throw new BreadCrumbException("Le fil d'Ariane ne "
+ "commence pas à l'accueil : " + bc);
set.clear();
this.bc = bc;
}
private BreadCrumb()
{
}
BreadCrumb reverse()
{
final BreadCrumb ret = new BreadCrumb();
ret.elements.addAll(elements);
Collections.reverse(ret.elements);
ret.bc = StringUtils.join(ret.elements, " ");
return ret;
}
public Iterator<BreadCrumbElement> iterator()
{
return elements.iterator();
}
@Override
public String toString()
{
return bc;
}
}
面包屑渲染器的界面:
public interface BreadCrumbRender
{
List<CTObjectBean> getBreadCrumb()
throws Throwable;
String getTopCategory();
String getMenuRoot();
String getContext();
}
上面接口的实现是我的问题的根源:
private class CategoryBreadCrumbRender
implements BreadCrumbRender
{
private final BreadCrumb bc;
private final CTObject object;
CategoryBreadCrumbRender(final CTObject object)
{
this.object = object;
final String property;
// FIELD_BC is declared as a private static final String earlier on.
// logger is also a private static final Logger
try {
property = object.getProperty(FIELD_BC);
} catch (Throwable throwable) {
logger.fatal("Impossible d'obtenir le champ " + FIELD_BC
+ " de l'objet", throwable);
bc = BreadCrumb.EMPTY;
return;
}
try {
bc = new BreadCrumb(property);
} catch (BreadCrumbException e) {
logger.fatal("Impossible d'obtenir le fil d'Ariane", e);
bc = BreadCrumb.EMPTY; // <-- HERE
}
}
// ....
在上面标记为// <-- HERE
的点上,我使用的Intellij IDEA和javac(1.6.0.29)都告诉我Variable bc might already have been assigned to
,这被认为是错误(实际上,代码确实是错误的)不编译)。
麻烦的是,我不明白为什么......我的理由如下:
.getProperty()
会抛出Throwable),当捕获到异常时,bc
被成功分配,然后我返回,到目前为止一直很好; bc
是最终的:分配不会发生(?)在try块中,但发生在catch块中...... 除了否,它没有。由于IDEA和javac都不同意我的意见,他们肯定是对的。但为什么呢?
(而且,BreadCrumb.EMPTY
在课堂上被宣布为private static final
,我想知道我怎么可以访问它...附属问题)
编辑:final
关键字(here存在已知错误,感谢@MiladNaseri链接到它),但是应该注意这一点错误,变量v
仅在catch
块中分配 - 但在上面的代码中,我在try
块中分配它,并且仅在catch
块中分配它抛出异常。此外,应该注意,错误仅发生在第二个catch
块中。
答案 0 :(得分:5)
好的,假设在第一个try块中,在执行property = object.getProperty(FIELD_BC);
时发生异常。因此,JVM将进入catch块,并在此过程中初始化bc
。
然后在第二个try块中,也会发生异常,导致BreadCrumb.EMPTY
被分配给bc
,从而有效地覆盖其原始值。
现在,这就是bc
可能的初始化方式。我希望你能看到我来自哪里。
由于JAVAC分析引擎没有区分try块内的一个或多个语句,因此它不会看到你的情况与下面的不同:
try {
bc = null;
String x = null;
System.out.println(x.toString());
} catch (Throwable e) {
bc = null;
}
在这种情况下,bc
将分配两次。换句话说,JAVAC不会关心Throwable
的来源所在,它只关心它可以存在,bc
可能在该try块中成功分配。
答案 1 :(得分:1)
我认为分析不够深入,无法真正理解try块中只有一个语句,无论如何都会发出诊断信息,这就是为什么你会在你的情况下看到它。
答案 2 :(得分:0)
请改为尝试:
BreadCrumb tmp = null;
try {
tmp = new BreadCrumb(property);
} catch (BreadCrumbException e) {
logger.fatal("Impossible d'obtenir le fil d'Ariane", e);
tmp = BreadCrumb.EMPTY;
}
bc = tmp;