类型信息
类型转换前的检查
- 向上转型
-
是一种安全的转换过程,如将circle转换成shape
- 向下转型
-
是一种不确定的转换过程,如将shape转换成circle,由于无法得知该shape对象具体是什么形状,故转换时首先要做类型检查
若进行了错误的类型转换会抛出ClassCastException异常,所以需要通过以下方式进行转前判断:
if(X instanceof Circle){ ((Circle)X).a(); }
使用类字面常量
通过Class.forName()获取类会马上执行类初始化,而使用类字面常量方式获取类,不会进行类初始化。
pubic class A{ public static boolean b=true; static{ System.out.println("发生了初始化"); } } System.out.println("c1:"); class c1=Class.forName("A.class"); System.out.println("b: "+b); System.out.println("c2:"); class c2=A.class; System.out.println("b: "+b);
c1:
发生了初始化
b:true
c2:
false类生命周期中的初始化可参看详解java类的生命周期
instanceOf与Class的等价性
eg:
class Base{} class Child extends Base{} public class BaseAndChild{ static void test(Object x){ System.out.println("x instanceof Base "+x instanceof Base); System.out.println("x instanceof Child "+x instanceof Child); System.out.println("x.isInstance(Base) "+x.isInstance(Base)); System.out.println("x.isInstance(Child) "+x.isInstance(Child)); System.out.println("x.getClass()=Base.class "+x.getClass()=Base.class); System.out.println("x.getClass()=Child.class "+x.getClass()=Child.class); } public static void main(String...args){ test(new Base()); test(new Child()); } }
true
false
true
false
true
false
true
true
true
true
false
true由上可知通过 ”==” 比较不存在继承关系
反射
RTTI(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
反射与RTTI的区别:
对RTTI来说,编译器在编译时打开和检查.class文件,而对于反射,.class文件在编译时时不可获取的,所以实在运行时打开和检查.class文件。
Methods.class
package reflect; public class Methods { public void a(){} private void b(){} public static void c() {} }
package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import static java.lang.System.out; public class JullyRe { public static void main(String...args) { try { Class c = Class.forName("reflect.Methods"); Method[] methods=c.getMethods(); Constructor[] constructors=c.getConstructors(); for(Method method:methods){ out.println("Methods:"+method.toString()); } for(Constructor constructor:constructors){ out.println("Construct:"+constructor.toString()); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
Methods:public static void reflect.Methods.c()
Methods:public void reflect.Methods.a()
Methods:public final void java.lang.Object.wait() throws java.lang.InterruptedException
Methods:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
Methods:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
Methods:public boolean java.lang.Object.equals(java.lang.Object)
Methods:public java.lang.String java.lang.Object.toString()
Methods:public native int java.lang.Object.hashCode()
Methods:public final native java.lang.Class java.lang.Object.getClass()
Methods:public final native void java.lang.Object.notify()
Methods:public final native void java.lang.Object.notifyAll()
Construct:public reflect.Methods()泛型
以下来自我的新博客文章泛型
泛型出现于Java SE5,泛型的出现使编写可以应用于多种类型的代码成为可能,实现了参数化类型,而且解耦了类或方法与使用的类型之间的约束。
eg:
List<T>
该List容器可装入的对象类型定义为泛型,即没有明确规定要装生命类型的对象
元组类库
当需要通过return语句返回多个对象时,可通过创建一个对象,将需要返回的多个对象存在该对象中,这种对象就称为元组
注意:
该对象只允许读取其中的元素,而不允许像其中存入新对象,这也被称为数据传送对象或行使
元组的应用:
FirstTuple:
public class FirstTuple <A,B>{ private final A a; private final B b; public FirstTuple(A a,B b){ this.a=a; this.b=b; } public A getA(){ return a; } public B getB(){ return b; } public String toString(){ return "A:"+a+" B:"+b; } }
SecondTuple:
public class SecondTuple<A ,B,C> extends FirstTuple<A,B>{ private final A a; private final B b; private final C c; public SecondTuple(A a,B b,C c){ super(a,b); this.a=a; this.b=b; this.c=c; } public A getA(){ return a; } public B getB(){ return b; } public C getC(){ return c; } public String toString(){ return "A:"+a+" B:"+b+" C:"+c; } }
TupleUser:
public class TupleUser { private static FirstTuple<String,Integer> f(String str,Integer inte){ return new FirstTuple<>(str,inte); } private static SecondTuple<String,Integer,String> s(String str,Integer inte,String stri){ return new SecondTuple<>(str,inte,stri); } public static void main(String...args){ System.out.println(f("fs",1)); System.out.println(s("ss",2,"ssi")); } }
由以上代码可知,通过泛型就可以灵活地将各种数据存入元组对象,数据类型可由客户端开发人员自己决定,而不用频繁地更改存储对象类
泛型的进一步使用
但不难发现以上功能通过定义超类Object类也可以实现,此时泛型的另一独到之处又体现出来,即是指定容器需要存放的对象类型,如:
List<String>
同时也可以实现”限定性的泛化”,即同时起到指定需要什么类型和先定义后决定用在限定范围内的具体类型。
应用如下:
public abstract class ThirdTuple<T extends FirstTuple<String,Integer>> { public abstract T f(); }
public class TupleUser extends ThirdTuple<TupleUser.ExtendFirstTuple>{ public static void main(String...args){ } /* error:String类型不满足定义的泛型约束(T extends FirstTuple<String,Integer>) @Override public String f() { return "f"; } */ @Override public ExtendFirstTuple f() { return new ExtendFirstTuple("str",1); } public class ExtendFirstTuple extends FirstTuple<String,Integer>{ public ExtendFirstTuple(String str, Integer inte) { super(str, inte); } } }
Android源码上的一个小应用:
@NonNull public abstract static class RecyclerView.Adapter<VH extends ViewHolder> { public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType); }
private class InerHolder extends RecyclerView.ViewHolder{ private TextView textView; private InerHolder(@NonNull View itemView) { super(itemView); textView=findViewById(R.id.recycler_textView); } } private class InerAdapter extends RecyclerView.Adapter<InerHolder>{ @NonNull @Override public InerHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { return new InerHolder(LayoutInflater.from(getApplicationContext()).inflate(R.layout.recycler_item,viewGroup,false)); } @Override public void onBindViewHolder(@NonNull InerHolder inerHolder, int i) { inerHolder.textView.setText("i:"+i); } @Override public int getItemCount() { return 1; } @Override public long getItemId(int position) { return super.getItemId(position); } }
若不使用泛型写法:
private class InerHolder extends RecyclerView.ViewHolder{ private TextView textView; private InerHolder(@NonNull View itemView) { super(itemView); textView=itemView.findViewById(R.id.recycler_textView); } } private class RecyclerAdapter extends RecyclerView.Adapter { private Context context; public RecyclerAdapter(Context context) { super(); this.context=context; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { return new InerHolder(LayoutInflater.from(context).inflate(R.layout.recycler_item,viewGroup,false)); } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { //此处若不想转型,可按第二种写法创建Adapter ((InerHolder)viewHolder).textView.setText("i:"+i); } @Override public long getItemId(int position) { return super.getItemId(position); } @Override public int getItemCount() { //输出一个Item return 1; } }
泛型方法
定义泛型方法秩序将泛型参数列表至于返回值前,eg:
public <T> void f(T t){}
杠杆利用类型参数推断
为了简化代码,可编写一个泛型工具类:
public class TUtil{ public static <k,V> Map<K,V> map(){ Map<K,V> map=new HashMap<K,V>(); } public static final void main(String...args){ Map<String,String> maper=TUtil.map(); } }
泛型方法中的可变参数
public class Ter{ public <T> void t(T...args){ for(T a:args){ } } }
泛型的擦除
Java泛型是使用擦出来实现的,在使用泛型时,任何具体的类型都会被擦除
如下代码:
class To{ public void f(){} } class Ter <T>{ public T t; public Ter(T t){ this.t=t; } public void fer(){ //Erro:cannot find symbol t.tf(); } } public class TerT{ public static final void main(String...args){ To to=new To(); Ter ter<To> ter=new Ter(to); ter.fer(); } }
由于To被擦除了类型,所以无法找到方法tf
解决方法
class Ter <T extends To>{ public T t; public Ter(T t){ this.t=t; } public void fer(){ //Erro:cannot find symbol t.tf(); } }
T -> T extends To
使其擦除到To边界