我正在学习Java考试,并且对静态和动态类型有疑问。
我有4个班级:A,B,C和Main。
public class A {
private void tell(){
System.out.println("AA");
}
}
public class B extends A {
public void tell(){
System.out.println("BB");
}
}
public class C extends B {
}
public class Main{
public static void main(String[] args) {
A c = new C();
c.tell();
}
}
我的建议是:输出应为“ BB”,因为c具有动态类型C。由于C没有“ tell”方法,所以使用上层B的方法,该方法打印“ BB”。
但是结果是一个错误,因为Java在A中寻找“ tell”。在A中它当然找不到,因为在那里被声明为priavte。但是为什么它看起来在A中,虽然只有它的静态类型是A,但是它的动态类型是C?
答案 0 :(得分:2)
您会收到一个错误消息,因为在编译时,编译器不知道将放置在A中的实际实例,因此,当编译器看到import * as React from 'react';
import { Component, ComponentClass, ComponentType } from 'react';
import {
DocumentReference,
Query,
CollectionReference,
DocumentSnapshotExpanded,
QuerySnapshotExpanded
} from './firemodel';
import { firestore } from 'firebase';
import { pick, forEach, isEqual, isFunction } from 'lodash';
import { expandDocSnapshot, expandQuerySnapshot } from 'modules/providers/util';
import SmartLoader from 'modules/atoms/SmartLoader';
type FirestoreQueryable<DataType> =
| DocumentReference<DataType>
| Query<DataType>
| CollectionReference<DataType>;
type FirestoryQueryableFunction<
DataType,
Props
> = (
firestore: firestore.Firestore,
props: Props,
) => Promise<FirestoreQueryable<DataType>>;
type QueryConfigEntry<Props> =
FirestoreQueryable<any> | FirestoryQueryableFunction<any, Props>;
type QueryConfig<Props> = {
[queryName: string]: QueryConfigEntry<Props>;
};
type FirestoreQueryableExpanded<Props, QE extends QueryConfigEntry<Props>> =
QE extends FirestoreQueryable<any> ? FirestoreQueryableExpanded1<QE> :
QE extends FirestoryQueryableFunction<any, Props> ? FirestoreQueryableExpanded1<ReturnType<QE>> : unknown;
type FirestoreQueryableExpanded1<QE extends FirestoreQueryable<any>> =
QE extends CollectionReference<infer DataType> | Query<infer DataType> ? QuerySnapshotExpanded<DataType> :
QE extends DocumentReference<infer DataType> ? DocumentSnapshotExpanded<DataType> : unknown;
interface WithFirestoreConfig<Props, PL extends keyof Props, Q extends QueryConfig<Props>> {
/** Object containing the queries to be provided to WrappedComponent.
* The queryName used is also the prop name the snapshot is passed in. */
queries: Q;
/** A list of props to whitelist passing to WrappedComponent.
* Configs without a list will whitelist all props */
props?: PL[];
/** Loading config items */
loading?: {
/** Number of ms after which to display the loading icon */
delay?: number;
/** Number of ms after which to display the timeout message */
timeout?: number;
};
}
type WithFirestoreHoC = <Props>() => <PL extends keyof Props, Q extends QueryConfig<Props>>(
config: WithFirestoreConfig<Props, PL, Q>,
) => (
WrappedComponent: ComponentType<WithFirestore<Props, Q> & Pick<Props, PL>>,
) => ComponentClass<Props, { error: Error; queries: {}; loaded: boolean }>;
const withFirestore: WithFirestoreHoC =
// An extra function call is needed so that callers can specify Props and
// still have PL and Q inferred. It can be removed when
// https://github.com/Microsoft/TypeScript/issues/10571 is implemented.
<Props extends {}>() =>
// Note: if `props` is not passed, there will be no inference for PL and it
// will default to its constraint, which is exactly the behavior we want as
// far as typing is concerned.
<PL extends keyof Props, Q extends QueryConfig<Props>>({
queries,
props: propPickList,
loading: { delay = 200, timeout = 0 } = {},
}: WithFirestoreConfig<Props, PL, Q>) => WrappedComponent =>
class WithFirestoreConnect extends Component<Props, { error: Error; queries: WithFirestore<Props, Q>; loaded: boolean }> {
subscriptions: {
[queryName: string]: ReturnType<FirestoreQueryable<any>['onSnapshot']>;
} = {};
state = {
error: null as Error,
queries: {} as WithFirestore<Props, Q>,
loaded: false,
};
componentDidMount() {
this.restartSubscription();
}
cancelSubscriptions = () => {
forEach(this.subscriptions, unsubscribe => unsubscribe());
this.subscriptions = {};
};
restartSubscription = () => {
// Open questions:
// - figuring out when all loaded (use a promise?)
this.cancelSubscriptions();
forEach(queries, async (q: QueryConfigEntry<Props>, key) => {
let ref: FirestoreQueryable<any>;
if (isFunction(q)) {
// The fact that this is an async/await means that we can
// create dependent queries within our FirestoreQueryableFunction
ref = await q(firestore(), this.props);
} else {
// Narrowing is not working for some reason.
ref = q as FirestoreQueryable<any>;
}
if (ref instanceof firestore.DocumentReference) {
this.subscriptions[key] = ref.onSnapshot(
snap => {
this.setState({
queries: Object.assign({}, this.state.queries, {[key]: expandDocSnapshot(snap)}),
});
},
err => {
console.error(JSON.stringify(err));
this.setState({ error: err });
this.cancelSubscriptions();
},
);
} else if (
ref instanceof firestore.CollectionReference ||
ref instanceof firestore.Query
) {
let ref2: {onSnapshot(os: (snap: firestore.QuerySnapshot) => void, oe: (err: Error) => void): () => void; } = ref;
this.subscriptions[key] = ref2.onSnapshot(
snap => {
this.setState({
queries: Object.assign({}, this.state.queries, {[key]: expandQuerySnapshot(snap)}),
});
},
err => {
console.error(JSON.stringify(err));
this.setState({ error: err });
this.cancelSubscriptions();
},
);
}
});
};
componentDidUpdate(prevProps: Props) {
if (!isEqual(this.props, prevProps)) {
this.restartSubscription();
}
}
componentWillUnmount() {
this.cancelSubscriptions();
}
render() {
if (!this.state.loaded || this.state.error) {
return (
<SmartLoader
error={this.state.error}
timeout={timeout}
delay={delay}
/>
);
}
const whitelistedProps = propPickList
? pick(this.props, propPickList)
: this.props;
// Unsure what's wrong here ~ Matt
let WrappedComponent2 = WrappedComponent as any;
return <WrappedComponent2 {...whitelistedProps} {...this.state.queries} />;
}
};
export type WithFirestore<Props, Q extends QueryConfig<Props>> = {
[queryName in keyof Q]: FirestoreQueryableExpanded<Props, Q[queryName]>;
}
export default withFirestore;
// EXAMPLE
interface MyDoc {
y: number
}
declare let myDocRef: DocumentReference<MyDoc>;
declare let myCollRef: CollectionReference<MyDoc>;
let wrapped = withFirestore<{x: string}>()({
queries: {
myDoc: myDocRef,
myColl: myCollRef
},
})((props) => { return <>{props.myDoc.data.y + props.myColl.docs[props.x].data.y}</>; });
时,他只看了c.tell()
类确实没有一种可行的A
方法。
了解这一点的一种方法是使用此示例:
tell()
您可以看到前两行都可以(根据您当前的思维逻辑)。 public class A {
private void tell(){
System.out.println("AA");
}
}
public class B extends A {
public void tell(){
System.out.println("BB");
}
}
public class C extends A {
}
public class Main{
public static void main(String[] args) {
A b = new B();
b.tell();
A c = new C();
c.tell();
}
}
的方法为B
,因此tell()
应该可以调用b
。但是,如果将完全相同的赋值与没有tell()
方法的C
的另一个子类一起使用,则您的逻辑将失败。 tell()
和A
都具有C
方法,因此程序突然调用了一个不存在或无法访问的方法。