【问专家】我怕专家不会还是问大家吧:关于用内部匿名类继承ArrayList并重写add方法后的疑惑。

GODProbe 2007-10-31 01:20:43
arraylist中add方法签名如下:
public boolean add(Object o)

我使用内部匿名类继承了arraylist并重写了其add方法,代码如下:(在arraylist上附加的功能很简单,就是我向arraylist中添加VO对象、添加时我根据添加新对象newer的Key属性判断、如果集合中已有这个Key的旧对象older则比较newer和older的Id,仅保留Id较大者,较小者如果是newer则不予添加如果是older则将其剔除)
ArrayList mylist = new ArrayList(){
private HashMap myUil = new HashMap();
public boolean add(Object o){
VO newer = (VO)o;
VO older = (VO)myUil.get(newer.getKey());
if(older!=null){
if(newer.getId()>older.getId()){
myUil.remove(older.getKey());
this.remove(older.getKey());
}else return false;
}
myUil.put(newer.getKey(), newer);
return super.add(newer);
}
public void remove(String Key){
Iterator e = iterator();
int i = 0;
while (e.hasNext()) {
VO older = (VO)e.next();
if (older.getKey().equals(Key)) {
super.remove(i);
return;
}
i++;
}
}
public String toString(){
return "myUil.size()=" + myUil.size() + "@list.size()=" + super.size();
}
};
以上代码工作正常,但是我觉得在add方法中接收Object参数我还得转成VO对象太麻烦,所以把add方法签名直接改为:
public boolean add(VO newer)
并将强制转换:VO newer = (VO)o; 去掉了,但是这样改以后在调用mylist.add(VO vo)方法的时候直接去调了父类也就是arraylist的add方法,导致我附加的功能失效。想来想去想不明白原因,比较郁闷,我觉得即使是:public boolean add(VO newer)这样的方法声明返回类型一样传参一样(VO类型也是Object啊)应该是重写了父类arrayist的add方法无疑啊,怎么会不认???
...全文
6657 39 打赏 收藏 转发到动态 举报
写回复
用AI写文章
39 条回复
切换为时间正序
请发表友善的回复…
发表回复
insiku 2007-11-07
  • 打赏
  • 举报
回复
GODProbe

莫非你认为sun用泛型来修正了这个所谓的bug?
泛型的加入根本就没有破坏java的原有体系 完全的无缝
ms的设计师的设计 完全就是为了满足个人便利
千里冰封820 2007-11-05
  • 打赏
  • 举报
回复
用泛型就可以解决了
千里冰封820 2007-11-05
  • 打赏
  • 举报
回复
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package test5;

import java.util.ArrayList;
import java.util.List;

/**
*
* @author hadeslee
*/
public class Test1 {


public Test1() {

}
public static void main(String[] args) throws Exception {
List<Test1> list=new ArrayList<Test1>(){
public boolean add(Test1 t){
System.out.println("添加了:"+t);
return super.add(t);
}
};
Test1 t=new Test1();
list.add(t);

}
}
GODProbe 2007-11-05
  • 打赏
  • 举报
回复
to:蠢人学编程
如果你的jdk1.5的测试代码真的输出那个结果:
import java.util.ArrayList;
class Vo{}
@SuppressWarnings("serial")
public class ArrayListTest {
static ArrayList<Vo> list = new ArrayList<Vo>(){
@Override
public boolean add(Vo v){
System.out.println("my add method overload");
return true;
}
};
public static void main(String[] args) {
Vo o = new Vo();
list.add(o);
}
}
OutPut:
my add method overload


看来sun的工程师用泛型改进了这个问题。
“tmd 越学越弱 不懂的太多了”可能会说:sun的设计师的口味果然与众不同
wangzhuming 2007-11-05
  • 打赏
  • 举报
回复
楼主的问题貌似在这里:ArrayList mylist = new ArrayList(){
insiku 2007-11-05
  • 打赏
  • 举报
回复
to sharpyuce
什么叫不可以重载?
不能通过编译那叫不可以
只不过对父类的持有者隐藏了重载的方法 这叫不可以重载?
只不过不能调用而已

完全可以通过别的方法来达到重载的目的



class A
{
public void methodA(Object o)
{}
}

A a = new A
{
public void methodA(Object o)
{
if(o instanceof String)
methodA((String)o);
else if(o instanceof Integer)
methodA((Integer)o);
else
super.methodA(o);
}

public void methodA(String s) //是不是重载出来的? 3个methodA的存在 难道不是重载???
{
}

public void methodA(Integer i) //是不是重载出来的? 只不过你不能直接调用而已
{
}
};



GODProbe 2007-11-05
  • 打赏
  • 举报
回复
“to GODProbe 重载是没有动态绑定的 回去研究下方法调用的相关过程就知道了”

——不必研究,想想不就知道?重载就没有必要动态绑定。你说的方法调用相关过程是个什么过程?好人做到底,大牛、继续启蒙启蒙我们这帮菜鸟啊。



to:蠢人学编程
——我的代码有点陷阱在里面,可能会误导你。没仔细看你的回帖,但是我的代码的问题和内部类无关、和匿名内部类也无关,纯粹是关于重写、重载的适用场景的。你要是混淆进匿名内部类的概念很容易搞混乱。“tmd越学越弱不懂的太多了”没有走过这个陷阱所以他显得不那么弱智。
我的结论是重写发生在父子类之间、而重载应该只在一个类内。其实这是一个初学java时的基础,不过学java的过程好像就是这样,刚开始学的面向对象、基本语法、多线程等等看似基础的东东才是做一个架构师必须深刻理解并反复实践的,反而是后来的jsp、struts、spring...就是个工具。
miniwei 2007-11-04
  • 打赏
  • 举报
回复


声明的为父类
匿名内部类没有实例
对于实例来说
他会向上转型为父类
所以内部类的方法对父类来说是隐藏了

所以如果对实例引用匿名内部类的方法
当然只会产生错误

因为父类根本不知道存在匿名内部类的方法
sharpyuce 2007-11-04
  • 打赏
  • 举报
回复
匿名内部类连类名都没有。怎么去让他以自己本身的引用,来生成对象啊
只能是父类或者接口的引用,来生成对象。。。
@SuppressWarnings("unused")
是没有被使用而出的警告;
@SuppressWarnings("serial")
是没有生成其序列ID而出的警告;
大哥这2种注释 貌似有什么大哎吗?

static ArrayList<? extends Object> list =
new ArrayList<Vo1>(){
..........................
};

你不以ArrayList或者更广的类型的引用来得到这个对象,(我们这里讨论的是匿名内部类)你说怎么得到对象,这个对象连类名都没有,何来他本类的引用??不能得到他自己的类的引用来生成自身对象,怎么调用他的重载方法,本人比较蠢,没听说过。。
还请楼上的高手请解答?或者你写一个匿名内部类可以重载的例子,只要能写出来,我就服!写不出来,还请大哥以后发贴时,思考下再反驳人家的观点。。。
insiku 2007-11-04
  • 打赏
  • 举报
回复
对于引用类型使用父类和使用子类这种情况在.net中有所改进,.net做了明确的语法语义区分:在写父类的时候可以用override修饰字来修饰方法表示该方法可以由子类重写、在写子类的时候可以直接重写这个方法也可以用new(此new非彼new)修饰字来修饰这个方法、new修饰字表示子类重新定义了这个方法而不重写父类方法。这么做以后在你使用这对父子类的时候如果用子类类型引用来接收对象则调用子类方法、如果用父类类型引用来接收对象则调用父类方法、互不影响。

==================

MS的设计师的口味果然与众不同
insiku 2007-11-04
  • 打赏
  • 举报
回复
...................................

to
GODProbe
重载是没有动态绑定的
回去研究下方法调用的相关过程就知道了


to
sharpyuce
不是不能重载 只是你的匿名类对象的持有者是其父类
其父类的方法表中 不存在你所新添加的方法 所以当然不允许你调用
另: 不要把所有的警告都@SuppressWarnings 掉 这是不好的习惯


例:

class A
{
public void methodA()
{}
}

class B extends A
{
public void methodA()
{}

public void methodB()
{}
}

class C extends A
{
public void methodA()
{}

public void methodC()
{}
}

public void testMethod(A a)
{
a.methodA(); // row 1
a.methodB(); // row 2
a.methodC(); // row 3
}



以上为大概的类结构
row 1 row 2 row 3 的写法正确吗?

row 2 row 3 肯定是错的
虽然你传给testMethod(A a)方法的参数可能是B对象 或者 是C对象
其内部确实存在methodB() 或者 methodC() 方法
但是参数的持有者是A 它不能保证你传过来的对象中一定存在methodB() 或者 methodC()



如果再理解不了 那还是转行吧
sharpyuce 2007-11-03
  • 打赏
  • 举报
回复
顶“别给我分”的结论
public boolean myAdd(VO v) {
// mylist.add(v); // 若使用这句则不能打印"A VO has been added."
new MyArrayListInner().add(v); // 使用这句可以
return true;
} // 初步结论是,匿名内部类中的方法不能实现重载


我有如下代码测试:

package alltest;

import java.util.ArrayList;

class Vo{

}

@SuppressWarnings("serial")
public class ArrayListTest {

static ArrayList<Vo> list = new ArrayList<Vo>(){
@Override
public boolean add(Vo v){
System.out.println("my add method overload");
return true;
}
};

public static void main(String[] args) {
Vo o = new Vo();
list.add(o);
}

}

OutPut:
my add method overload

注:JDK1.5
真的不能重载吗?上面的例子只能证明可以Override
声明为范性也是Override不是OverLoad

我采用JDK1。5以上的范性进行重写,对add,get进行重写,证明OverLoad:

package alltest;

import java.util.ArrayList;
class Vo1{

}

@SuppressWarnings({ "serial", "unchecked" })
public class ArrayListTest2 {

static ArrayList<? extends Object> list =
new ArrayList<Vo1>(){

@SuppressWarnings("unused")
public boolean add(){
System.out.println("My add Method is transfered");
return true;
}
public void get(){
System.out.println("My get Method is transfered");
}
};
public static void main(String[] args) {
/**
* The method get(int) in the type
* ArrayList<capture#2-of ? extends Object>
* is not applicable for the arguments ()
*/
list.add(); //报错,无法使用
list.get(); //报错,无法使用

}
}


结论:匿名内部类不能重载,至于什么原因,愿听高手解答!!



GODProbe 2007-11-03
  • 打赏
  • 举报
回复
在用spring写框架代码的时候遇到过这个问题,未及整理。因为spring几乎全部是用父类甚至接口引用接收子类实例(由spring在运行时注入),这个问题其实与内部类无关,而是关于重写、重载的适用场景的。
基础很重要、今天再次固本培源,分大家均分。
GODProbe 2007-11-03
  • 打赏
  • 举报
回复
让我们抛开匿名的问题,看看如下最简单的代码:
import java.util.*;
public class TestTmd {

class MyArrayListInner extends ArrayList {//内部类(其实内不内部类都一样)
public boolean add(Object v) {——这样写可以打印(这是重写)
//public boolean add(VO v) {——这样写不会打印(这是重载、其实不应该这么做)

System.out.println("A VO has been added.");
return true;
}
}

public void myAdd(VO v) {
ArrayList inner = new MyArrayListInner();
inner.add(v);
}

public static void main(String[] args) {
new TestTmd().myAdd(new VO());
}
}
class VO {}

得出的结论是重写发生在父子类之间、而重载应该只在一个类内。谢谢大家帮我补习一下基础。
GODProbe 2007-11-02
  • 打赏
  • 举报
回复
“给人的感觉是编译器和jre不一致。”——不应该这么说、应该说编译器和jre在内部匿名类上犯同一个错误。都是内部类匿名和不匿名的行为不一致!
GODProbe 2007-11-02
  • 打赏
  • 举报
回复
谢谢“别给我分”替我做的测试。

TO: tmd越学越弱不懂的太多了
我还不是很懂你说的意思,你说的是在编译后发生的事情,我疑惑的是为什么到了运行时仍然会有问题。你说的引用类型的问题是编译后的问题,到了运行时即使是匿名内部类jre也应该识别正确的引用类型——那就是子类——这才叫动态绑定、否则还谈什么多态?我的代码说明匿名内部类对父类方法重写可以重载就不行这是不对的,既然重写的方法可以在运行时动态绑定到子类方法为什么重载的就绑定不到?给人的感觉是编译器和jre不一致。都是内部类、匿名不匿名不应该影响行为。

对于引用类型使用父类和使用子类这种情况在.net中有所改进,.net做了明确的语法语义区分:在写父类的时候可以用override修饰字来修饰方法表示该方法可以由子类重写、在写子类的时候可以直接重写这个方法也可以用new(此new非彼new)修饰字来修饰这个方法、new修饰字表示子类重新定义了这个方法而不重写父类方法。这么做以后在你使用这对父子类的时候如果用子类类型引用来接收对象则调用子类方法、如果用父类类型引用来接收对象则调用父类方法、互不影响。这一点在java中我还不知道能不能实现、也简单、做个测试就知道。

“匿名内部类的实例不可能拥有自己的类型,而永远都是父类的类型。”——这不是真理,只是编译器的局限。
malligator 2007-11-02
  • 打赏
  • 举报
回复
你用了匿名内部类
所以声明的时候得用ArrayList(或者其父类/借口)

这样,这个声明的引用怎么知道实现了public boolean add(VO newer)的实现啊(它不知道它指向的是一个自定义的实现了public boolean add(VO newer)的子类啊)
,所以匹配到了ArrayList中的public boolean add(Object object)了
Dan1980 2007-11-02
  • 打赏
  • 举报
回复
呵呵。本以为楼主是来结贴的。没想到你还是没明白。

首先,你应该明白一个基本常识:对象向上转型以后,会隐藏其父类中没有的接口(公共方法)。
举例:

class Base {
void f() { }
}

class Inherited extends Base {
void f() { }
void g() { }
}

public class Test {
public static void main(String[] args) {
Inherited i1 = new Inherited();
i1.f(); // 没有问题
i1.g(); // 也没有问题

Base i2 = new Inherited();
i2.f(); // 没有问题
i2.g(); // 编译错误!无法访问g(),应为i2向上转型为Base,而g()不是Base的接口之一,从而被隐藏。
}
}

现在回到匿名内部类。
举例:

import java.util.*;
public class Test {
public static void main(String[] args) {
class ArrayListInner extends ArrayList {
void f() { }
}
ArrayListInner list1 = new ArrayListInner();

ArrayList list2 = new ArrayList() {
void f() { }
};

// list1是一般内部类的实例,而list2是匿名内部类的实例

list1.f(); // 可以访问f()
list2.f(); // 编译错误!不可以访问f()
}
}

上面的原因很明显。list2被声明为ArrayList类型,但f()并不是ArrayList的接口,所以被隐藏了。而list1是ArrayListInner类型的,具有ArrayListInner类的所有接口,包括f()。对于list2,你无法将它声明为它的“直接类”的类型,这是因为这个“直接类”是没有名字的。这不是什么语言或编译器的局限,这是匿名内部类本身的局限,或者更准确地说,是它的一个特点。

不知楼主这下是否明白了。
GODProbe 2007-11-01
  • 打赏
  • 举报
回复
谢谢,那么我写的add方法签名: public boolean add(VO newer) 应该是对arraylist中add方法的一种重载。
重载的概念更明确,那为什么我调用mylist.add(VO vo)方法的时候直接去调了父类也就是arraylist的add方法?
bug挺正常,jsk1.4中在某些机器上使用String.split()方法不正常就是bug。
我试试hh写的代码先。
Dan1980 2007-11-01
  • 打赏
  • 举报
回复
哈哈,完全同意。这个问题的根源在于:

匿名内部类的实例不可能拥有自己的类型,而永远都是父类的类型。
加载更多回复(19)

62,628

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧